├── .env ├── .github └── workflows │ ├── archive_lint_reports.yml │ ├── kubernetes-deploy-to-cluster.yml │ ├── latest.yml │ ├── nightly-did-lint-check.yml │ ├── nightly-did-test-suite.yml │ └── release.yml ├── .gitignore ├── .gitlab-ci.yml ├── LICENSE ├── README.md ├── ci ├── deploy-k8s-aws │ ├── Dockerfile │ ├── README.md │ ├── app-specs │ │ ├── configmap-uni-resolver-frontend.yaml │ │ ├── deployment-uni-resolver-frontend.yaml │ │ └── deployment-uni-resolver-web.yaml │ ├── namespace │ │ ├── namespace-setup.sh │ │ └── namespace-setup.yaml │ └── scripts │ │ ├── driver-config.py │ │ ├── driver-config.yaml │ │ ├── entrypoint.sh │ │ ├── k8s-template.yaml │ │ ├── prepare-deployment.py │ │ └── substitute-btcr-driver-values.py ├── did-lint-check │ └── run.sh ├── docker-build-push │ ├── Dockerfile │ ├── README.md │ └── entrypoint.sh ├── get-driver-status │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── action.yaml │ ├── app │ │ ├── get-driver-status.py │ │ └── requirements.txt │ └── entrypoint.sh └── run-did-test-suite │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── action.yml │ ├── app │ ├── index.js │ ├── local-files-utils.js │ ├── package-lock.json │ ├── package.json │ ├── testserver-utils.js │ └── utils.js │ └── entrypoint.sh ├── docker-compose.yml ├── docs ├── branching-strategy.md ├── continuous-integration-and-delivery.md ├── design-goals.md ├── dev-system.md ├── driver-development.md ├── figures │ ├── architecture.png │ ├── aws-architecture.png │ ├── overview.png │ └── protocol.png ├── java-components.md ├── logo-dif.png ├── logo-ngi0pet.png └── troubleshooting.md ├── driver-http ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── uniresolver │ └── driver │ └── http │ └── HttpDriver.java ├── driver ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── uniresolver │ └── driver │ ├── AbstractDriver.java │ ├── Driver.java │ ├── servlet │ ├── InitServlet.java │ ├── PropertiesServlet.java │ ├── ResolveServlet.java │ └── ServletUtil.java │ └── util │ ├── HttpBindingServerUtil.java │ └── MediaTypeUtil.java ├── examples ├── .gitignore ├── pom.xml ├── sovrin │ ├── danube.txn │ ├── lib │ │ └── .gitignore │ ├── localhost.txn │ └── mainnet.txn └── src │ └── main │ ├── java │ └── uniresolver │ │ └── examples │ │ ├── TestClientUniResolver.java │ │ ├── TestDriverDidBtcr.java │ │ ├── TestDriverDidSov.java │ │ ├── TestLocalUniDereferencer.java │ │ ├── TestLocalUniResolver.java │ │ └── w3ctestsuite │ │ ├── TestDIDResolution.java │ │ ├── TestDIDURLDereferencing.java │ │ ├── TestIdentifier.java │ │ └── TestSuiteUtil.java │ └── resources │ └── log4j2.properties ├── openapi └── openapi.yaml ├── pom.xml ├── uni-resolver-client ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── uniresolver │ └── client │ ├── ClientUniDereferencer.java │ └── ClientUniResolver.java ├── uni-resolver-core ├── .gitignore ├── openapi │ └── java-client-generated │ │ ├── .gitignore │ │ └── .openapi-generator-ignore ├── pom.xml └── src │ └── main │ └── java │ └── uniresolver │ ├── DereferencingException.java │ ├── ResolutionException.java │ ├── UniDereferencer.java │ ├── UniResolver.java │ ├── result │ ├── DereferenceResult.java │ ├── ResolveResult.java │ └── Result.java │ ├── util │ └── HttpBindingClientUtil.java │ └── w3c │ ├── DIDResolver.java │ └── DIDURLDereferencer.java ├── uni-resolver-local ├── .gitignore ├── LICENSE ├── pom.xml └── src │ └── main │ └── java │ └── uniresolver │ └── local │ ├── LocalUniDereferencer.java │ ├── LocalUniResolver.java │ ├── configuration │ └── LocalUniResolverConfigurator.java │ └── extensions │ ├── DereferencerExtension.java │ ├── ExtensionStatus.java │ ├── ResolverExtension.java │ ├── impl │ ├── DIDDocumentExtension.java │ ├── DummyDereferencerExtension.java │ └── DummyResolverExtension.java │ └── util │ └── ExecutionStateUtil.java └── uni-resolver-web ├── .gitignore ├── docker └── Dockerfile ├── pom.xml └── src ├── main ├── java │ └── uniresolver │ │ └── web │ │ ├── WebUniResolver.java │ │ ├── WebUniResolverApplication.java │ │ ├── WebUniResolverWebServerFactoryCustomizer.java │ │ ├── config │ │ ├── DriverConfigs.java │ │ ├── ServletMappings.java │ │ └── WebAppConfig.java │ │ └── servlet │ │ ├── MethodsServlet.java │ │ ├── PropertiesServlet.java │ │ ├── ResolveServlet.java │ │ ├── ServletUtil.java │ │ ├── TestIdentifiersServlet.java │ │ └── TraitsServlet.java └── resources │ ├── application-dev.yml │ ├── application.yml │ └── log4j2.xml └── test └── resources ├── jetty-env.xml ├── jetty.xml └── log4j2-test.xml /.env: -------------------------------------------------------------------------------- 1 | uniresolver_driver_did_btcr_bitcoinConnection=blockcypherapi 2 | uniresolver_driver_did_btcr_rpcUrlMainnet=http://user:pass@localhost:8332/ 3 | uniresolver_driver_did_btcr_rpcUrlTestnet=http://user:pass@localhost:18332/ 4 | uniresolver_driver_did_btcr_rpcCertMainnet= 5 | uniresolver_driver_did_btcr_rpcCertTestnet= 6 | 7 | uniresolver_driver_did_sov_libIndyPath= 8 | uniresolver_driver_did_sov_poolConfigs=_;./sovrin/_.txn;test;./sovrin/test.txn;builder;./sovrin/builder.txn;danube;./sovrin/danube.txn;idunion;./sovrin/idunion.txn;idunion:test;./sovrin/idunion-test.txn;indicio;./sovrin/indicio.txn;indicio:test;./sovrin/indicio-test.txn;indicio:demo;./sovrin/indicio-demo.txn;nxd;./sovrin/nxd.txn;findy:test;./sovrin/findy-test.txn 9 | uniresolver_driver_did_sov_poolVersions=_;2;test;2;builder;2;danube;2;idunion;2;idunion:test;2;indicio;2;indicio:test;2;indicio:demo;2;nxd;2;findy:test;2 10 | uniresolver_driver_did_sov_walletNames=_;w1;test;w2;builder;w3;danube;w4;idunion;w5;idunion:test;w6;indicio;w7;indicio:test;w8;indicio:demo;w9;nxd;w11;findy:test;w12 11 | uniresolver_driver_did_sov_submitterDidSeeds=_;_;test;_;builder;_;danube;_;idunion;_;idunion:test;_;indicio;_;indicio:test;_;indicio:demo;_;nxd;_;findy:test;_ 12 | 13 | uniresolver_driver_did_erc725_ethereumConnections=mainnet;hybrid;ropsten;hybrid;rinkeby;hybrid;kovan;hybrid 14 | uniresolver_driver_did_erc725_rpcUrls=mainnet;https://mainnet.infura.io/v3/fd9e225bc1234f49b48b295c611078eb;ropsten;https://ropsten.infura.io/v3/fd9e225bc1234f49b48b295c611078eb;rinkeby;https://rinkeby.infura.io/v3/fd9e225bc1234f49b48b295c611078eb;kovan;https://kovan.infura.io/v3/fd9e225bc1234f49b48b295c611078eb 15 | uniresolver_driver_did_erc725_etherscanApis=mainnet;http://api.etherscan.io/api;ropsten;http://api-ropsten.etherscan.io/api;rinkeby;http://api-rinkeby.etherscan.io/api;kovan;http://api-kovan.etherscan.io/api 16 | 17 | uniresolver_driver_did_work_apikey=sxVQUoDE015VhAs5ep4b57DFA5vT3zqvf1Dm1sGe 18 | uniresolver_driver_did_work_domain=https://credentials.id.workday.com 19 | 20 | uniresolver_driver_kilt_blockchain_node=wss://spiritnet.kilt.io 21 | 22 | uniresolver_driver_dns_dnsServers= 23 | 24 | uniresolver_driver_did_indy_libIndyPath= 25 | uniresolver_driver_did_indy_poolConfigs=sovrin;./sovrin/sovrin.txn;sovrin:test;./sovrin/sovrin-test.txn;sovrin:builder;./sovrin/sovrin-builder.txn;danube;./sovrin/danube.txn;idunion;./sovrin/idunion.txn;idunion:test;./sovrin/idunion-test.txn;indicio;./sovrin/indicio.txn;indicio:test;./sovrin/indicio-test.txn;indicio:demo;./sovrin/indicio-demo.txn;nxd;./sovrin/nxd.txn;findy:test;./sovrin/findy-test.txn 26 | uniresolver_driver_did_indy_poolVersions=sovrin;2;sovrin:test;2;sovrin:builder;2;danube;2;idunion;2;idunion:test;2;indicio;2;indicio:test;2;indicio:demo;2;nxd;2;findy:test;2 27 | uniresolver_driver_did_indy_walletNames=sovrin;w1;sovrin:test;w2;sovrin:builder;w3;danube;w4;idunion;w5;idunion:test;w6;indicio;w7;indicio:test;w8;indicio:demo;w9;nxd;w11;findy:test;w12 28 | uniresolver_driver_did_indy_submitterDidSeeds=sovrin;_;sovrin:test;_;sovrin:builder;_;danube;_;idunion;_;idunion:test;_;indicio;_;indicio:test;_;indicio:demo;_;nxd;_;findy:test;_ 29 | 30 | uniresolver_driver_did_echo_mainnet_rpc_url=http://127.0.0.1:8090/rpc 31 | uniresolver_driver_did_echo_testnet_rpc_url=http://testnet.echo-dev.io 32 | uniresolver_driver_did_echo_devnet_rpc_url=http://127.0.0.1:8090/rpc 33 | 34 | DID_METHOD_HOST_URL=0.0.0.0:8102 35 | DID_METHOD_TLS_SYSTEMCERTPOOL=true 36 | DID_METHOD_MODE=resolver 37 | 38 | uniresolver_driver_did_icon_node_url=https://sejong.net.solidwallet.io/api/v3 39 | uniresolver_driver_did_icon_score_addr=cxc7c8b0bb85eca64aecc8cc38628c4bc3c449f1fd 40 | uniresolver_driver_did_icon_network_id=83 41 | 42 | LEDGIS_LIT_ENDPOINT=https://lit.ledgis.io 43 | LEDGIS_LIT_CODE=lit 44 | 45 | ORB_DRIVER_HOST_URL=0.0.0.0:8121 46 | ORB_DRIVER_TLS_SYSTEMCERTPOOL=true 47 | ORB_DRIVER_VERIFY_RESOLUTION_RESULT_TYPE=all 48 | 49 | uniresolver_driver_did_dns_dnsServers= 50 | uniresolver_driver_did_dns_didKeyResolver=https://dev.uniresolver.io/1.0/ 51 | 52 | uniresolver_driver_did_com_network=https://lcd-mainnet.commercio.network 53 | 54 | uniresolver_driver_did_ev_node_url=https://polygon-mumbai.g.alchemy.com/v2/jLMUummm16stzMQjW1OB79IwuDjsJqS7 55 | uniresolver_driver_did_ev_address_im=0x4E4f55190185f2694D331E5c9Fd70a2B75Eb4Bd2 56 | uniresolver_driver_did_ev_base_blocks=2700000 57 | 58 | uniresolver_driver_did_itn_resolverUrl=https://resolver.itn.mobi 59 | 60 | uniresolver_driver_did_iota_iotaNodeEndpoint=https://api.stardust-mainnet.iotaledger.net/ 61 | uniresolver_driver_did_iota_iotaNodeAuthToken= 62 | uniresolver_driver_did_iota_smrNodeEndpoint=https://api.shimmer.network/ 63 | uniresolver_driver_did_iota_smrNodeAuthToken= 64 | uniresolver_driver_did_iota_customNetworkName=rms 65 | uniresolver_driver_did_iota_customNodeEndpoint=https://api.testnet.shimmer.network/ 66 | uniresolver_driver_did_iota_customNodeAuthToken= 67 | 68 | uniresolver_driver_did_quarkid_node_url=https://lbquarkid2.extrimian.com/ 69 | uniresolver_driver_did_quarkid_node_pattern=did:quarkid 70 | uniresolver_driver_did_quarkid_node_behavior=1 71 | uniresolver_driver_did_quarkid_node_threadpool_size=240000 -------------------------------------------------------------------------------- /.github/workflows/archive_lint_reports.yml: -------------------------------------------------------------------------------- 1 | name: Copy Result JSON on Commit 2 | 3 | on: 4 | push: 5 | branches: 6 | - did-lint-reports 7 | paths: 8 | - 'result.json' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | copy-result-json: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout source code 16 | uses: actions/checkout@v4 17 | 18 | - name: Get current timestamp 19 | id: timestamp 20 | uses: nanzm/get-time-action@master 21 | with: 22 | timeZone: UTC 23 | format: 'YYYY-MM-DD-HH-mm-ss' 24 | 25 | - name: Install dependencies 26 | run: sudo apt-get install jq -y 27 | 28 | - name: Configure git user 29 | run: | 30 | git config --global user.name 'Kim Duffy' 31 | git config --global user.email 'kimdhamilton@gmail.com' 32 | 33 | - name: Copy result.json with timestamp 34 | run: | 35 | cp result.json result_${{ steps.timestamp.outputs.time }}.json 36 | jq . result_${{ steps.timestamp.outputs.time }}.json > tmp.json && mv tmp.json result_${{ steps.timestamp.outputs.time }}.json 37 | 38 | - name: Push to target repository 39 | env: 40 | REPO_ACCESS_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }} 41 | run: | 42 | git clone https://x-access-token:${REPO_ACCESS_TOKEN}@github.com/decentralized-identity/universal-resolver-lint-dashboard.git 43 | cd universal-resolver-lint-dashboard 44 | git checkout -b new-result || git checkout new-result 45 | git branch --set-upstream-to=origin/new-result new-result 46 | git pull 47 | mv ../result_${{ steps.timestamp.outputs.time }}.json . 48 | git add result_${{ steps.timestamp.outputs.time }}.json 49 | git commit -m "Add new result file" 50 | git push -u origin new-result 51 | 52 | 53 | -------------------------------------------------------------------------------- /.github/workflows/kubernetes-deploy-to-cluster.yml: -------------------------------------------------------------------------------- 1 | name: AWS Kubernetes deployment 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@master 11 | 12 | - name: Import Secrets 13 | uses: hashicorp/vault-action@v2.3.0 14 | with: 15 | url: ${{ secrets.VAULT_ADDR }} 16 | token: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 17 | caCertificate: ${{ secrets.VAULTCA }} 18 | secrets: | 19 | ci/data/gh-workflows/universal-resolver-cluster dif-cluster-kube-config | KUBE_CONFIG_DATA ; 20 | ci/data/gh-workflows/universal-resolver-cluster aws-access-key-id | AWS_ACCESS_KEY_ID ; 21 | ci/data/gh-workflows/universal-resolver-cluster aws-secret-access-key | AWS_SECRET_ACCESS_KEY ; 22 | ci/data/gh-workflows/universal-resolver-cluster rpc-url-testnet | RPC_URL_TESTNET ; 23 | ci/data/gh-workflows/universal-resolver-cluster rpc-cert-testnet | RPC_CERT_TESTNET ; 24 | ci/data/gh-workflows/deployment-status slack-webhook-url | SLACK_WEBHOOK_URL 25 | 26 | - name: Deploy to AWS 27 | uses: ./ci/deploy-k8s-aws 28 | env: 29 | KUBE_CONFIG_DATA: ${{secrets.KUBE_CONFIG_DATA_BASE64_UNI_RESOLVER_PROD}} 30 | AWS_ACCESS_KEY_ID: ${{env.AWS_ACCESS_KEY_ID}} 31 | AWS_SECRET_ACCESS_KEY: ${{env.AWS_SECRET_ACCESS_KEY}} 32 | RPC_URL_TESTNET: ${{env.RPC_URL_TESTNET}} 33 | RPC_CERT_TESTNET: ${{env.RPC_CERT_TESTNET}} 34 | 35 | - name: Slack notification 36 | if: failure() # Send message only on failure 37 | uses: 8398a7/action-slack@v3 38 | with: 39 | status: ${{ job.status }} 40 | fields: repo,commit,action,eventName,ref,workflow # selectable (default: repo,message) 41 | env: 42 | SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }} # required 43 | -------------------------------------------------------------------------------- /.github/workflows/latest.yml: -------------------------------------------------------------------------------- 1 | name: Maven snapshot and Docker latest image 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '.gitignore' 7 | - 'README.md' 8 | - 'LICENSE' 9 | - 'docs' 10 | branches: [ main, 'test-driver-**' ] 11 | workflow_dispatch: 12 | 13 | jobs: 14 | maven-snapshot: 15 | uses: danubetech/workflows/.github/workflows/maven-snapshot.yml@main 16 | with: 17 | MAVEN_REPO_SERVER_ID: 'danubetech-maven-snapshots' 18 | secrets: 19 | VAULT_ADDR: ${{ secrets.VAULT_ADDR }} 20 | CI_SECRET_READER_PERIODIC_TOKEN: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 21 | VAULTCA: ${{ secrets.VAULTCA }} 22 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 23 | 24 | docker-latest: 25 | needs: [ maven-snapshot ] 26 | uses: danubetech/workflows/.github/workflows/docker-latest.yml@main 27 | with: 28 | GLOBAL_IMAGE_NAME: universalresolver/uni-resolver-web 29 | GLOBAL_REPO_NAME: docker.io 30 | PATH_TO_DOCKERFILE: uni-resolver-web/docker/Dockerfile 31 | secrets: 32 | VAULT_ADDR: ${{ secrets.VAULT_ADDR }} 33 | CI_SECRET_READER_PERIODIC_TOKEN: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 34 | VAULTCA: ${{ secrets.VAULTCA }} 35 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 36 | 37 | trigger-deployment: 38 | needs: [ docker-latest ] 39 | runs-on: ubuntu-latest 40 | steps: 41 | 42 | - name: Get current branch 43 | id: branch 44 | run: | 45 | echo "branch_name=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT 46 | 47 | - name: Trigger AWS Kubernetes deployment 48 | uses: actions/github-script@v6 49 | env: 50 | BRANCH: ${{ steps.branch.outputs.branch_name }} 51 | with: 52 | github-token: ${{ secrets.GITHUB_TOKEN }} 53 | script: | 54 | await github.rest.actions.createWorkflowDispatch({ 55 | owner: context.repo.owner, 56 | repo: context.repo.repo, 57 | workflow_id: 'kubernetes-deploy-to-cluster.yml', 58 | ref: process.env.BRANCH 59 | }) 60 | -------------------------------------------------------------------------------- /.github/workflows/nightly-did-lint-check.yml: -------------------------------------------------------------------------------- 1 | name: Nightly DID Lint check 2 | 3 | on: 4 | schedule: 5 | - cron: '0 6,12,18,0 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | did-lint-check: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@master 13 | - name: Did Lint check 14 | run: ./ci/did-lint-check/run.sh 15 | 16 | - name: Slack notification 17 | uses: 8398a7/action-slack@v3 18 | with: 19 | status: ${{ job.status }} 20 | fields: repo,commit,action,eventName,ref,workflow 21 | env: 22 | SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }} 23 | if: failure() 24 | -------------------------------------------------------------------------------- /.github/workflows/nightly-did-test-suite.yml: -------------------------------------------------------------------------------- 1 | name: Nightly did-test-suite 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | driver-health-check: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@master 11 | - name: Import Secrets 12 | uses: hashicorp/vault-action@v2.3.0 13 | with: 14 | url: ${{ secrets.VAULT_ADDR }} 15 | token: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 16 | caCertificate: ${{ secrets.VAULTCA }} 17 | secrets: | 18 | ci/data/gh-workflows/deployment-status slack-webhook-url | SLACK_WEBHOOK_URL 19 | - name: Get driver status 20 | uses: ./ci/get-driver-status 21 | with: 22 | host: https://dev.uniresolver.io 23 | out: /home/runner/work/universal-resolver/universal-resolver/driver-status-reports 24 | keep_result: true 25 | # - name: Run did-test-suite 26 | # uses: ./ci/run-did-test-suite 27 | # with: 28 | # host: https://did-test-suite.uniresolver.io/test-suite-manager/generate-report 29 | # driver_status_report: ${{ env.driver_status_report }} 30 | # reports_folder: ${{ env.reports_folder }} 31 | - name: Slack notification 32 | uses: 8398a7/action-slack@v3 33 | with: 34 | status: ${{ job.status }} 35 | fields: repo,commit,action,eventName,ref,workflow 36 | env: 37 | SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }} 38 | if: failure() 39 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Maven and Docker release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | release_type: 7 | description: 'Major, Minor or Patch release' 8 | type: choice 9 | required: true 10 | default: 'minor' 11 | options: 12 | - "major" 13 | - "minor" 14 | - "patch" 15 | 16 | jobs: 17 | maven-release: 18 | uses: danubetech/workflows/.github/workflows/maven-release.yml@main 19 | with: 20 | MAVEN_REPO_SERVER_ID: 'danubetech-maven-releases' 21 | RELEASE_TYPE: ${{ github.event.inputs.release_type }} 22 | secrets: 23 | VAULT_ADDR: ${{ secrets.VAULT_ADDR }} 24 | CI_SECRET_READER_PERIODIC_TOKEN: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 25 | VAULTCA: ${{ secrets.VAULTCA }} 26 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 27 | 28 | docker-release: 29 | needs: maven-release 30 | uses: danubetech/workflows/.github/workflows/maven-triggered-docker-release.yml@main 31 | with: 32 | GLOBAL_IMAGE_NAME: universalresolver/uni-resolver-web 33 | GLOBAL_REPO_NAME: docker.io 34 | GLOBAL_FRAMEWORK: triggered 35 | PATH_TO_DOCKERFILE: uni-resolver-web/docker/Dockerfile 36 | secrets: 37 | VAULT_ADDR: ${{ secrets.VAULT_ADDR }} 38 | CI_SECRET_READER_PERIODIC_TOKEN: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 39 | VAULTCA: ${{ secrets.VAULTCA }} 40 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target/ 5 | /bin/ 6 | docker-compose.override.yml 7 | /.idea/ 8 | *.iml 9 | .vscode/ 10 | .factorypath 11 | Makefile 12 | **/deploy/ -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: docker:latest 2 | 3 | services: 4 | - docker:dind 5 | 6 | before_script: 7 | - echo -n $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY 8 | 9 | build-master: 10 | stage: build 11 | script: 12 | - cd resolver/java/uni-resolver-web/ 13 | - docker build -f ./docker/Dockerfile --pull -t "$CI_REGISTRY_IMAGE" . 14 | - docker push "$CI_REGISTRY_IMAGE" 15 | only: 16 | - master 17 | 18 | build: 19 | stage: build 20 | script: 21 | - cd resolver/java/uni-resolver-web/ 22 | - docker build -f ./docker/Dockerfile --pull -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" . 23 | - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" 24 | except: 25 | - master 26 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8.0-slim 2 | 3 | LABEL "name"="Deployment of the Universal Resolver to a Kubernetes Cluster" 4 | LABEL "maintainer"="Bernhard Fuchs " 5 | LABEL "version"="1.0.0" 6 | 7 | LABEL "com.github.actions.name"="GitHub Action for deploying the Universal Resolver" 8 | LABEL "com.github.actions.description"="Deployes the Universal Resolver to a Kubernetes cluster." 9 | LABEL "com.github.actions.icon"="package" 10 | LABEL "com.github.actions.color"="blue" 11 | 12 | RUN apt-get update -y && \ 13 | apt-get install -y curl gnupg openssh-client git && \ 14 | curl -Lso /bin/aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/1.11.5/2018-12-06/bin/linux/amd64/aws-iam-authenticator && \ 15 | chmod +x /bin/aws-iam-authenticator && \ 16 | curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.23.6/bin/linux/amd64/kubectl && \ 17 | chmod +x ./kubectl && \ 18 | mv ./kubectl /usr/local/bin/kubectl && \ 19 | apt-get -y clean && apt-get -y autoclean && apt-get -y autoremove && \ 20 | pip install setuptools awscli 21 | 22 | COPY app-specs /app-specs 23 | COPY namespace /namespace 24 | COPY scripts / 25 | RUN chmod +x /entrypoint.sh 26 | ENTRYPOINT ["/entrypoint.sh"] 27 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Deployment of the Universal Resolver 2 | 3 | ## Usage with Docker 4 | Setting the environment: 5 | 6 | export KUBE_CONFIG_DATA=$(cat /home/pp/dev/devops/universal-resolver-kubernetes/aws/danubetech-dev-cluster10/danubetech-dev-cluster10-KUBE_CONFIG_DATA.txt) 7 | 8 | Build: 9 | 10 | docker build -t ur-deployer . 11 | 12 | Run: 13 | 14 | docker run -t ur-deployer 15 | 16 | 17 | ## Usage via script 18 | Setting the environment: 19 | 20 | export KUBECONFIG=/home/pp/dev/devops/universal-resolver-kubernetes/aws/danubetech-dev-cluster10/kubeconfig-danubetech-dev-cluster10.yaml 21 | 22 | Run: 23 | 24 | cd scripts 25 | ./entrypoint.sh 26 | 27 | 28 | ## Usage as GitHub Action 29 | 30 | Github Action workflow-file: 31 | 32 | ``` 33 | name: CI/CD Workflow for universal-resolver 34 | 35 | on: 36 | push: 37 | branches: 38 | - master 39 | pull_request: 40 | branches: 41 | - master 42 | 43 | jobs: 44 | build: 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@master 48 | - name: Deploy to AWS 49 | uses: philpotisk/universal-resolver-k8s-deployment@master 50 | env: 51 | KUBE_CONFIG_DATA: ${{secrets.KUBE_CONFIG_DATA}} 52 | AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}} 53 | AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}} 54 | ``` -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/app-specs/configmap-uni-resolver-frontend.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: uni-resolver-frontend 5 | namespace: uni-resolver 6 | data: 7 | backend_url: https://dev.uniresolver.io/ 8 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/app-specs/deployment-uni-resolver-frontend.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: uni-resolver-frontend 5 | namespace: uni-resolver 6 | labels: 7 | app: uni-resolver 8 | type: frontend 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: uni-resolver-frontend 14 | template: 15 | metadata: 16 | labels: 17 | app: uni-resolver-frontend 18 | spec: 19 | containers: 20 | - name: uni-resolver-frontend 21 | image: universalresolver/universal-resolver-frontend 22 | imagePullPolicy: Always 23 | ports: 24 | - containerPort: 7081 25 | env: 26 | - name: BACKEND_URL 27 | valueFrom: 28 | configMapKeyRef: 29 | name: uni-resolver-frontend 30 | key: backend_url 31 | --- 32 | apiVersion: v1 33 | kind: Service 34 | metadata: 35 | name: uni-resolver-frontend 36 | namespace: uni-resolver 37 | spec: 38 | type: NodePort 39 | selector: 40 | app: uni-resolver-frontend 41 | ports: 42 | - protocol: TCP 43 | port: 7081 44 | targetPort: 7081 45 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/app-specs/deployment-uni-resolver-web.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: uni-resolver-web 5 | namespace: uni-resolver 6 | labels: 7 | app: uni-resolver 8 | type: backend 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: uni-resolver-web 14 | template: 15 | metadata: 16 | labels: 17 | app: uni-resolver-web 18 | spec: 19 | containers: 20 | - name: uni-resolver-web 21 | image: universalresolver/uni-resolver-web 22 | imagePullPolicy: Always 23 | ports: 24 | - containerPort: 8080 25 | livenessProbe: 26 | httpGet: 27 | path: "/1.0/methods" 28 | port: 8080 29 | initialDelaySeconds: 30 30 | periodSeconds: 10 31 | timeoutSeconds: 5 32 | --- 33 | apiVersion: v1 34 | kind: Service 35 | metadata: 36 | name: uni-resolver-web 37 | namespace: uni-resolver 38 | spec: 39 | type: NodePort 40 | selector: 41 | app: uni-resolver-web 42 | ports: 43 | - protocol: TCP 44 | port: 8080 45 | targetPort: 8080 46 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/namespace/namespace-setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | kubectl create -f namespace-setup.yaml 4 | CLUSTER_NAME=$(kubectl config view -o=jsonpath='{.clusters[0].name}') 5 | USER_NAME=$(kubectl config view -o=jsonpath='{.users[0].name}') 6 | 7 | kubectl config set-context uni-resolver --namespace=uni-resolver \ 8 | --cluster="$CLUSTER_NAME" \ 9 | --user="$USER_NAME" 10 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/namespace/namespace-setup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: uni-resolver 5 | labels: 6 | name: uni-resolver 7 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/scripts/driver-config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import os 4 | import sys 5 | import yaml 6 | 7 | 8 | def append_to_container(config, driver, spec): 9 | for entry in config[driver]: 10 | spec['spec']['template']['spec']['containers'][0][entry] = config[driver][entry] 11 | return spec 12 | 13 | 14 | def add_config(config): 15 | for driver in config: 16 | spec_list = [] 17 | with open('./deploy/deployment-' + driver + '.yaml', 'r+') as spec_file: 18 | for spec in yaml.load_all(spec_file, Loader=yaml.FullLoader): 19 | 20 | if spec['kind'] == 'Deployment': 21 | changed_spec = append_to_container(config, driver, spec) 22 | print(changed_spec) 23 | spec_list.append(spec) 24 | else: 25 | spec_list.append(spec) 26 | 27 | spec_file.close() 28 | 29 | with open('./deploy/deployment-' + driver + '.yaml', 'w') as spec_file: 30 | yaml.dump_all(spec_list, spec_file, default_flow_style=False) 31 | spec_file.close() 32 | 33 | 34 | def main(argv): 35 | config = yaml.load(open('driver-config.yaml', 'r'), Loader=yaml.FullLoader) 36 | add_config(config) 37 | 38 | 39 | if __name__ == "__main__": 40 | main(sys.argv[1:]) 41 | print('%s script finished' % os.path.basename(__file__)) 42 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/scripts/driver-config.yaml: -------------------------------------------------------------------------------- 1 | orb-did-driver: 2 | args: ["start"] 3 | env: 4 | - name: ORB_DRIVER_HOST_URL 5 | value: 0.0.0.0:8121 6 | - name: ORB_DRIVER_TLS_SYSTEMCERTPOOL 7 | value: "true" 8 | - name: ORB_DRIVER_VERIFY_RESOLUTION_RESULT_TYPE 9 | value: "all" 10 | driver-did-btcr: 11 | env: 12 | - name: uniresolver_driver_did_btcr_bitcoinConnection 13 | value: btcd 14 | - name: uniresolver_driver_did_btcr_rpcUrlTestnet 15 | value: RPC_URL_TESTNET 16 | - name: uniresolver_driver_did_btcr_rpcCertMainnet 17 | value: 18 | - name: uniresolver_driver_did_btcr_rpcCertTestnet 19 | value: RPC_CERT_TESTNET 20 | cheqd-did-driver: 21 | env: 22 | - name: MAINNET_ENDPOINT 23 | value: "grpc.cheqd.net:443,true,5s" 24 | - name: TESTNET_ENDPOINT 25 | value: "grpc.cheqd.network:443,true,5s" 26 | - name: LOG_LEVEL 27 | value: "warn" 28 | - name: RESOLVER_LISTENER 29 | value: "0.0.0.0:8080" 30 | driver-did-ev: 31 | env: 32 | - name: NODE_HOST 33 | value: "https://polygon-mumbai.g.alchemy.com/v2/jLMUummm16stzMQjW1OB79IwuDjsJqS7" 34 | - name: ADDRESS_IM 35 | value: "0x4E4f55190185f2694D331E5c9Fd70a2B75Eb4Bd2" 36 | - name: BASE_BLOCKS 37 | value: "2700000" 38 | driver-did-itn: 39 | env: 40 | - name: DID_RESOLVER_URL 41 | value: "https://resolver.itn.mobi" 42 | driver-did-iota: 43 | env: 44 | - name: IOTA_NODE_ENDPOINT 45 | value: "https://api.stardust-mainnet.iotaledger.net/" 46 | - name: IOTA_SMR_NODE_ENDPOINT 47 | value: "https://api.shimmer.network/" 48 | - name: IOTA_CUSTOM_NETWORK_NAME 49 | value: "rms" 50 | - name: IOTA_CUSTOM_NODE_ENDPOINT 51 | value: "https://api.testnet.shimmer.network/" 52 | driver-did-prism: 53 | env: 54 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/scripts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "#### Kubernetes Deployment of the Universal Resolver ####" 4 | 5 | set -e 6 | 7 | echo "## Root folder ##" 8 | ls -al / 9 | 10 | echo "$KUBE_CONFIG_DATA" | base64 --decode > /tmp/config 11 | export KUBECONFIG=/tmp/config 12 | 13 | cp /*.py /*.yaml -r /app-specs -r /namespace . 2>/dev/null || : 14 | 15 | echo "Current workspace Folder" 16 | pwd 17 | ls -al . 18 | python --version 19 | python prepare-deployment.py 20 | 21 | echo "Driver config script" 22 | python driver-config.py 23 | 24 | echo "Apply did-btcr secrets" 25 | python substitute-btcr-driver-values.py --url "$RPC_URL_TESTNET" --cert "$RPC_CERT_TESTNET" 26 | 27 | echo "## Deployment Folder ##" 28 | cd deploy 29 | cat deployment-driver-did-btcr.yaml 30 | ls -al . 31 | 32 | echo "### Deploying following Specs ### " 33 | cat deploy.sh 34 | 35 | 36 | ./deploy.sh 37 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/scripts/k8s-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{containerName}} 5 | namespace: uni-resolver 6 | labels: 7 | app: {{containerName}} 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: {{containerName}} 13 | template: 14 | metadata: 15 | labels: 16 | app: {{containerName}} 17 | spec: 18 | containers: 19 | - name: {{containerName}} 20 | image: {{containerTag}} 21 | imagePullPolicy: Always 22 | resources: 23 | requests: 24 | memory: "128Mi" 25 | cpu: "5m" 26 | limits: 27 | memory: "512Mi" 28 | cpu: "500m" 29 | ports: 30 | - containerPort: {{containerPort}} 31 | securityContext: 32 | capabilities: 33 | drop: 34 | - NET_RAW 35 | --- 36 | apiVersion: v1 37 | kind: Service 38 | metadata: 39 | name: {{containerName}} 40 | namespace: uni-resolver 41 | spec: 42 | type: NodePort 43 | selector: 44 | app: {{containerName}} 45 | ports: 46 | - protocol: TCP 47 | port: {{containerPort}} 48 | targetPort: {{containerPort}} 49 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/scripts/substitute-btcr-driver-values.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import getopt 4 | 5 | 6 | def substitute_values(file_content, url, cert): 7 | file_content = file_content.replace('RPC_URL_TESTNET', url).replace('RPC_CERT_TESTNET', cert) 8 | print(file_content) 9 | return file_content 10 | 11 | 12 | def main(argv): 13 | url: str = "" 14 | cert: str = "" 15 | 16 | try: 17 | opts, args = getopt.getopt(argv, "u:c", ["url=", "cert="]) 18 | except getopt.GetoptError: 19 | sys.exit(2) 20 | 21 | for opt, arg in opts: 22 | if opt in ("-u", "--url"): 23 | url = arg 24 | elif opt in ("-c", "--cert"): 25 | cert = arg 26 | 27 | btcr_deployment_read = open('./deploy/deployment-driver-did-btcr.yaml', 'rt') 28 | file_content = btcr_deployment_read.read() 29 | btcr_deployment_read.close() 30 | 31 | updated_content = substitute_values(file_content, url, cert) 32 | 33 | btcr_deployment_write = open('./deploy/deployment-driver-did-btcr.yaml', 'wt') 34 | btcr_deployment_write.write(updated_content) 35 | btcr_deployment_write.close() 36 | 37 | 38 | if __name__ == "__main__": 39 | main(sys.argv[1:]) 40 | print('%s script finished' % os.path.basename(__file__)) 41 | -------------------------------------------------------------------------------- /ci/did-lint-check/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Pulling did-status-generator image" 4 | docker pull oydeu/did-status-generator:latest 5 | 6 | echo "Running did-status-generator image" 7 | docker run --rm -e "DID_RESOLVER=https://resolver.identity.foundation/1.0/identifiers/" oydeu/did-status-generator:latest > result.json 8 | echo "Set result.json to result_var" 9 | result_var=$(cat result.json) 10 | 11 | echo "Checkout to did-lint-reports branch" 12 | git fetch 13 | git switch did-lint-reports --force 14 | 15 | echo "Replace old result.json with result_var" 16 | echo "$result_var" > result.json 17 | 18 | echo "Push result file to repo" 19 | git config --global user.email "admin@danubetech.com" 20 | git config --global user.name "DID Lint check workflow" 21 | git status 22 | git add result.json 23 | git commit -m "DID Lint check reports" 24 | git push origin did-lint-reports:did-lint-reports 25 | -------------------------------------------------------------------------------- /ci/docker-build-push/Dockerfile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FROM docker:stable 3 | 4 | LABEL "name"="Docker build/push Action" 5 | LABEL "maintainer"="Phil " 6 | LABEL "version"="1.0.0" 7 | 8 | LABEL "com.github.actions.name"="Docker build/push Action" 9 | LABEL "com.github.actions.description"="GitHub Action for building and pushing a Docker container" 10 | LABEL "com.github.actions.icon"="package" 11 | LABEL "com.github.actions.color"="blue" 12 | 13 | ADD entrypoint.sh /entrypoint.sh 14 | RUN chmod +x /entrypoint.sh 15 | ENTRYPOINT ["/entrypoint.sh"] 16 | -------------------------------------------------------------------------------- /ci/docker-build-push/README.md: -------------------------------------------------------------------------------- 1 | # Github Action 2 | 3 | GitHub Action for building and publishing a Docker container to Docker Hub. 4 | 5 | 6 | ## Secrets 7 | 8 | - `DOCKER_USERNAME` - *Required* Name of Docker Hub user which has **Write access** 9 | - `DOCKER_PASSWORD` - *Required* Password of the Docker Hub user 10 | 11 | Setup secrets in your GitHub project at "Settings > Secrets" 12 | 13 | ## Environment Variables 14 | 15 | 16 | - `CONTAINER_TAG` : **mandatory**, example: 'universalresolver/driver-did-btcr:latest' 17 | - `DOCKER_FILE` : **optional**, default is **Dockerfile** 18 | 19 | 20 | ## Example 21 | 22 | 23 | ```yaml 24 | name: CI/CD Workflow for driver-did-btcr 25 | 26 | on: 27 | push: 28 | branches: 29 | - master 30 | pull_request: 31 | branches: 32 | - master 33 | 34 | jobs: 35 | build: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@master 39 | - name: Docker Build and Push 40 | uses: .ci/docker-build-push 41 | env: 42 | DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}} 43 | DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}} 44 | DOCKER_FILE: docker/Dockerfile 45 | CONTAINER_TAG: universalresolver/driver-did-btcr:latest 46 | ``` 47 | 48 | ## LICENSE 49 | 50 | Copyright (c) 2020 51 | 52 | Licensed under the Apache2 License. -------------------------------------------------------------------------------- /ci/docker-build-push/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -l 2 | set -e 3 | 4 | echo "DOCKER BUILD AND PUSH" 5 | 6 | echo "Environment: " 7 | echo "- BUILD_PATH: ${BUILD_PATH}"; 8 | echo "- DOCKER_FILE: ${DOCKER_FILE}"; 9 | echo "- CONTAINER_TAG: ${CONTAINER_TAG}"; 10 | echo "- GITHUB_EVENT_NAME: ${GITHUB_EVENT_NAME}"; 11 | echo "- GITHUB_EVENT_PATH: ${GITHUB_EVENT_PATH}"; 12 | echo "- GITHUB_REF: ${GITHUB_REF}"; 13 | echo "- GITHUB_WORKSPACE: ${GITHUB_WORKSPACE}"; 14 | echo "- GITHUB_REPOSITORY: ${GITHUB_REPOSITORY}"; 15 | echo "- GITHUB_ACTOR: ${GITHUB_ACTOR}"; 16 | 17 | if [[ -z "$GITHUB_EVENT_NAME" ]]; then 18 | echo "Set the GITHUB_EVENT_NAME env variable." 19 | exit 1 20 | fi 21 | 22 | if [[ -z "$GITHUB_EVENT_PATH" ]]; then 23 | echo "Set the GITHUB_EVENT_PATH env variable." 24 | exit 1 25 | fi 26 | 27 | if [[ -z "$GITHUB_REF" ]]; then 28 | echo "Set the GITHUB_REF env variable." 29 | exit 1 30 | fi 31 | 32 | if [ -z "${DOCKER_FILE}" ] 33 | then 34 | echo "No Dockerfile specified. Using default file: Dockerfile" 35 | DOCKER_FILE=Dockerfile 36 | fi 37 | 38 | if [[ -z "$CONTAINER_TAG" ]]; then 39 | echo "Set the CONTAINER_TAG env variable." 40 | exit 1 41 | fi 42 | 43 | ls -al 44 | 45 | if [ -n "${BUILD_PATH}" ] 46 | then 47 | cd ${BUILD_PATH} 48 | fi 49 | 50 | ls -al 51 | 52 | echo "Building container ..." 53 | docker build . -f ${DOCKER_FILE} -t ${CONTAINER_TAG} 54 | 55 | if [ -n "${DOCKER_USERNAME}" ] 56 | then 57 | echo "Pushing container to DockerHub ..." 58 | 59 | docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD} 60 | 61 | docker push ${CONTAINER_TAG} 62 | fi 63 | 64 | -------------------------------------------------------------------------------- /ci/get-driver-status/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | /app/config.json 3 | /app/driver-status-result-*.json 4 | -------------------------------------------------------------------------------- /ci/get-driver-status/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-buster 2 | 3 | LABEL "com.github.actions.name"="Get driver status" 4 | LABEL "com.github.actions.description"="Get driver status for a Universal Resolver deplyoment" 5 | LABEL "com.github.actions.icon"="mic" 6 | LABEL "com.github.actions.color"="blue" 7 | LABEL "version"="1.0.0" 8 | LABEL "repository"="https://github.com/decentralized-identity/universal-resolver" 9 | LABEL "homepage"="https://uniresolver.io" 10 | LABEL "maintainer"="Bernhard Fuchs " 11 | 12 | RUN apt-get update && \ 13 | apt-get upgrade -y 14 | 15 | WORKDIR /get-driver-status/ 16 | 17 | COPY app/requirements.txt . 18 | RUN pip install --no-cache-dir -r requirements.txt 19 | 20 | COPY app/get-driver-status.py . 21 | 22 | COPY entrypoint.sh / 23 | RUN chmod +x /entrypoint.sh 24 | ENTRYPOINT ["/entrypoint.sh"] 25 | -------------------------------------------------------------------------------- /ci/get-driver-status/README.md: -------------------------------------------------------------------------------- 1 | # universal-resolver-deployment-driver-status 2 | 3 | This tool can be used as a standalone script for testing an existing deployment of the Uni Resolver, it can be integrated as a [github action](https://github.com/features/actions) into a github workflow, or it can be run as docker container. 4 | 5 | ## Run get-driver-status script manually 6 | 7 | python get-driver-status.py --host --config --out --write200 false 8 | 9 | The script needs Python 3 and dependencies listed in the `requirements.txt`. 10 | 11 | Automatic installation of requirements: 12 | 13 | pip install --no-cache-dir -r requirements.txt 14 | 15 | All arguments are optional and default values are aligned with github actions workflow. 16 | 17 | Default values: 18 | 19 | host: https://dev.uniresolver.io 20 | config: /github/workspace/config.json 21 | out: ./ 22 | write200: True 23 | 24 | 25 | ## Use action in github workflow 26 | 27 | - name: Get Driver Status 28 | uses: ./ci/driver-status 29 | with: 30 | host: :// 31 | config: 32 | out folder: 33 | debug: 34 | write200: 35 | 36 | Example can be seen in the [uni-resolver workflow configuration](https://github.com/decentralized-identity/universal-resolver/blob/master/.github/workflows/universal-resolver-build-deploy.yml) 37 | 38 | ## Run as docker container 39 | ### Build container with 40 | 41 | docker build -f Dockerfile -t get-driver-status . 42 | 43 | ### Run container with 44 | 45 | docker run -it --rm -e HOST= -e CONFIG_FILE= --name get-driver-status . 46 | -------------------------------------------------------------------------------- /ci/get-driver-status/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'uni-resolver-get-driver-status' 2 | description: 'Get driver status for Universal Resolver deployment' 3 | inputs: 4 | host: 5 | description: 'Host of Uni-Resolver deployment' 6 | required: false 7 | default: https://dev.uniresolver.io 8 | config: 9 | description: 'Uni-Resolver configuration file' 10 | required: false 11 | default: /github/workspace/uni-resolver-web/src/main/resources/application.yml 12 | out: 13 | description: 'Folder location of driver-status-result- file' 14 | required: false 15 | default: /github/workspace/uni-resolver-web/driver-status-reports 16 | debug: 17 | description: 'Enhance logging' 18 | required: false 19 | default: false 20 | keep_result: 21 | description: 'Keep result file' 22 | required: false 23 | default: false 24 | runs: 25 | using: 'docker' 26 | image: 'Dockerfile' 27 | args: 28 | - ${{ inputs.host }} 29 | - ${{ inputs.config }} 30 | - ${{ inputs.out_folder }} 31 | -------------------------------------------------------------------------------- /ci/get-driver-status/app/get-driver-status.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import datetime 4 | import logging 5 | import re 6 | import json 7 | import getopt 8 | import asyncio 9 | import yaml 10 | from aiohttp import ClientSession 11 | 12 | 13 | WRITE_SUCCESS: bool = True 14 | 15 | logging.basicConfig( 16 | format="%(asctime)s %(levelname)s:%(name)s: %(message)s", 17 | level=logging.DEBUG, 18 | datefmt="%H:%M:%S", 19 | stream=sys.stderr, 20 | ) 21 | logger = logging.getLogger("areq") 22 | logging.getLogger("chardet.charsetprober").disabled = True 23 | 24 | 25 | # Create Test Data START 26 | def parse_json_to_dict(path): 27 | with open(path) as file: 28 | raw_config = yaml.safe_load(file) 29 | return raw_config 30 | 31 | 32 | def extract_did_method(did): 33 | return re.findall("(?<=:)(.*?)(?=:)", did)[0] 34 | 35 | 36 | def create_test_data(drivers_config, host): 37 | test_data = [] 38 | for driver in drivers_config: 39 | for testIdentifier in driver["testIdentifiers"]: 40 | if testIdentifier.startswith("did:"): 41 | driver_test_data = { 42 | "method": extract_did_method(testIdentifier), 43 | "url": host + testIdentifier 44 | } 45 | test_data.append(driver_test_data) 46 | 47 | return test_data 48 | 49 | 50 | # Create Test Data END 51 | 52 | # Run tests START 53 | async def fetch_html(url: str, session: ClientSession): 54 | resp = await session.request(method="GET", url=url) 55 | plain_html = await resp.text() 56 | logger.info("Got response [%s] for URL: %s", resp.status, url) 57 | logger.info("With body:\n %s", plain_html) 58 | 59 | if resp.status == 200: 60 | did_document = json.loads(plain_html) 61 | logger.info("With didDocument:\n %s", did_document) 62 | result = {"status": resp.status, "resolutionResponse": {}} 63 | result["resolutionResponse"]["application/did+ld+json"] = did_document 64 | return result 65 | else: 66 | return {"status": resp.status, "error": plain_html} 67 | 68 | 69 | async def write_one(results, data, session): 70 | url = data['url'] 71 | try: 72 | res = await fetch_html(url=url, session=session) 73 | if WRITE_SUCCESS | res['status'] != 200: 74 | results.update({url: res}) 75 | except asyncio.TimeoutError: 76 | results.update({ 77 | url: { 78 | "status": 504, 79 | "body": "Gateway Timeout error" 80 | } 81 | }) 82 | logger.info("Gateway Timeout error for %s", url) 83 | print("\n-----------------------------------------------------------------------------------------------\n") 84 | 85 | 86 | async def run_tests(test_data): 87 | async with ClientSession() as session: 88 | tasks = [] 89 | results = {} 90 | for data in test_data: 91 | tasks.append( 92 | write_one(results, data=data, session=session) 93 | ) 94 | await asyncio.gather(*tasks) 95 | return results 96 | 97 | 98 | # Run tests END 99 | 100 | def main(argv): 101 | help_text = './get-driver-status.py -host -config -out ' \ 102 | '--write200 ' 103 | host = 'https://dev.uniresolver.io' 104 | config = '/github/workspace/uni-resolver-web/src/main/resources/application.yml' 105 | out = './' 106 | try: 107 | opts, args = getopt.getopt(argv, "h:c:o:w", ["host=", "config=", "out=", "write200="]) 108 | except getopt.GetoptError: 109 | print(help_text) 110 | sys.exit(2) 111 | for opt, arg in opts: 112 | if opt == '--help': 113 | print(help_text) 114 | sys.exit() 115 | elif opt in ("-h", "--host"): 116 | host = arg 117 | elif opt in ("-c", "--config"): 118 | config = arg 119 | elif opt in ("-o", "--out"): 120 | print("ARG:" + arg) 121 | out = arg + '/' 122 | elif opt in ("-w", "--write200"): 123 | global WRITE_SUCCESS 124 | if arg.lower() == 'false': 125 | WRITE_SUCCESS = False 126 | 127 | uni_resolver_path = host + "/1.0/identifiers/" 128 | print('Resolving for: ' + uni_resolver_path) 129 | 130 | # build test data 131 | config_dict = parse_json_to_dict(config) 132 | test_data = create_test_data(config_dict["uniresolver"]["drivers"], uni_resolver_path) 133 | 134 | # run tests 135 | results = asyncio.run(run_tests(test_data=test_data)) 136 | 137 | results_timestamp = datetime.datetime.utcnow().replace(microsecond=0).isoformat() 138 | filename = "driver-status-" + results_timestamp + ".json" 139 | print('Out folder: ' + out) 140 | out_path = out + filename 141 | print('Writing to path: ' + out_path) 142 | with open(out_path, "a") as f: 143 | f.write(json.dumps(results, indent=4, sort_keys=True)) 144 | 145 | 146 | if __name__ == "__main__": 147 | main(sys.argv[1:]) 148 | print('Script finished') 149 | -------------------------------------------------------------------------------- /ci/get-driver-status/app/requirements.txt: -------------------------------------------------------------------------------- 1 | aiofiles==0.6.0 2 | aiohttp==3.8.5 3 | async-timeout==3.0.1 4 | pyyaml==5.4 5 | -------------------------------------------------------------------------------- /ci/get-driver-status/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "#### Driver Status for a Universal Resolver Deployment ####" 4 | 5 | echo "Running with parameters:" 6 | sh -c "echo $*" 7 | 8 | echo "host: $INPUT_HOST" 9 | echo "config: $INPUT_CONFIG" 10 | echo "out folder: $INPUT_OUT" 11 | echo "debug: $INPUT_DEBUG" 12 | echo "keep result: $INPUT_KEEP_RESULT" 13 | 14 | if "$INPUT_DEBUG"; then 15 | echo "Current folder" 16 | pwd 17 | ls -al 18 | 19 | echo "Deployment folder" 20 | ls -al deploy 21 | 22 | echo "Root folder" 23 | ls -al / 24 | 25 | echo "#### Ingress file ####" 26 | cat /github/workspace/deploy/uni-resolver-ingress.yaml 27 | fi 28 | 29 | DATE_WITH_TIME=$(TZ=UTC date "+%Y-%m-%d_%H:%M:%S") 30 | REPORTS_FOLDER="$INPUT_OUT/nightly-run-$DATE_WITH_TIME" 31 | mkdir -p "$REPORTS_FOLDER" 32 | 33 | python --version 34 | python /get-driver-status/get-driver-status.py --host "$INPUT_HOST" --config "$INPUT_CONFIG" --out "$REPORTS_FOLDER" 35 | 36 | echo "Switch to drivers-status-reports branch" 37 | git config --global --add safe.directory /github/workspace 38 | git fetch 39 | git checkout -b driver-status-reports origin/driver-status-reports 40 | 41 | if "$INPUT_KEEP_RESULT"; 42 | then 43 | echo "Push result file to repo" 44 | git config --global user.email "admin@danubetech.com" 45 | git config --global user.name "Get driver status workflow" 46 | # Pass driver_status_report to next step in github action 47 | echo "driver_status_report=$(git diff --name-only --staged)" >> "$GITHUB_ENV" 48 | echo "reports_folder=$REPORTS_FOLDER" >> "$GITHUB_ENV" 49 | git add . 50 | git commit -m "Get driver status results" 51 | git push origin driver-status-reports:driver-status-reports 52 | else 53 | cat -b /driver-status-reports/driver-status-*.json 54 | fi 55 | -------------------------------------------------------------------------------- /ci/run-did-test-suite/.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules -------------------------------------------------------------------------------- /ci/run-did-test-suite/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine3.13 2 | 3 | LABEL "com.github.actions.name"="Did Test Suite" 4 | LABEL "com.github.actions.description"="Run the did-test-suite against the api of a Universal Resolver deployment" 5 | LABEL "com.github.actions.icon"="mic" 6 | LABEL "com.github.actions.color"="blue" 7 | LABEL "version"="1.0.0" 8 | LABEL "repository"="https://github.com/decentralized-identity/universal-resolver" 9 | LABEL "homepage"="https://uniresolver.io" 10 | LABEL "maintainer"="Bernhard Fuchs " 11 | 12 | RUN apk update && apk upgrade && \ 13 | apk add --no-cache git 14 | 15 | WORKDIR /run-did-test-suite/ 16 | 17 | COPY app/package.json . 18 | RUN npm install 19 | 20 | COPY app/index.js . 21 | COPY app/local-files-utils.js . 22 | COPY app/testserver-utils.js . 23 | COPY app/utils.js . 24 | 25 | COPY entrypoint.sh / 26 | RUN chmod +x /entrypoint.sh 27 | ENTRYPOINT ["/entrypoint.sh"] -------------------------------------------------------------------------------- /ci/run-did-test-suite/README.md: -------------------------------------------------------------------------------- 1 | # run-did-test-suite action 2 | 3 | Developed with Node 14.x and without guarantee of backwards compatibility. 4 | 5 | ## env variables 6 | `MODE`: 7 | - `server`(default) Run against a did-test-suite server 8 | - `local` Create local files 9 | 10 | `DRIVER_STATUS_REPORT`: 11 | - Path to file, generated by [get-driver-status](https://github.com/decentralized-identity/universal-resolver/tree/driver-status-reporting/ci/get-driver-status) action 12 | 13 | `OUTPUT_PATH`: 14 | - In `server` mode: Path to write the did-test-suite report 15 | - In `local` mode: Path to write the created files 16 | 17 | `HOST`: 18 | - Only for `server` mode and required 19 | - Full url of deployed did-test-suite endpoint e.g. `https://did-test-suite.uniresolver.io/test-suite-manager/generate-report` 20 | 21 | `GENERATE_DEFAULT_FILE`: 22 | - Only for `local` mode and optional 23 | - If `true` script automatically creates a `default.js` file with all the resolver related files in the `OUTPUT_PATH` 24 | 25 | ### Server mode to run against hosted did-test-suite example 26 | 27 | ```bash 28 | node index.js --DRIVER_STATUS_REPORT=/driver-status-2021-05-19_15-08-15-UTC.json --HOST=https://did-test-suite.uniresolver.io/test-suite-manager/generate-report 29 | ``` 30 | 31 | ### Local mode to create manual files example 32 | 33 | ```bash 34 | node index.js --MODE=local --DRIVER_STATUS_REPORT=/driver-status-2021-05-19_15-08-15-UTC.json --OUTPUT_PATH= --GENERATE_DEFAULT_FILE=true 35 | ``` 36 | 37 | ### Github action example 38 | 39 | **Note:** Env variables for `driver_status_report` and `reports_folder` are set by previous step in this case. See [example](https://github.com/decentralized-identity/universal-resolver/blob/ccbbbf17946ff62b53f647734c382dc460661659/ci/get-driver-status/entrypoint.sh#L43) in `get-driver-status` action. 40 | 41 | ```yaml 42 | - name: Run did-test-suite 43 | uses: ./ci/run-did-test-suite 44 | with: 45 | host: https://did-test-suite.uniresolver.io/test-suite-manager/generate-report 46 | driver_status_report: ${{ env.driver_status_report }} 47 | reports_folder: ${{ env.reports_folder }} 48 | ``` 49 | -------------------------------------------------------------------------------- /ci/run-did-test-suite/action.yml: -------------------------------------------------------------------------------- 1 | name: 'uni-resolver-did-test-suite' 2 | description: 'Run did-test-suite against Universal Resolver deployment' 3 | inputs: 4 | host: 5 | description: 'Host of Uni-Resolver deployment' 6 | required: false 7 | driver_status_report: 8 | description: 'File with testset of Uni-resolver response' 9 | required: false 10 | reports_folder: 11 | description: 'Destination folder of reports' 12 | required: true 13 | runs: 14 | using: 'docker' 15 | image: 'Dockerfile' 16 | args: 17 | - ${{ inputs.host }} 18 | - ${{ inputs.driver_status_report }} 19 | - ${{ inputs.reports_folder }} -------------------------------------------------------------------------------- /ci/run-did-test-suite/app/index.js: -------------------------------------------------------------------------------- 1 | const core = require('@actions/core'); 2 | const github = require('@actions/github'); 3 | const fs = require('fs'); 4 | const argv = require('minimist')(process.argv.slice(2)); 5 | 6 | const { runTests } = require("./testserver-utils"); 7 | const { generateLocalFile, generateDefaultFile } = require("./local-files-utils"); 8 | const { resetTestData, createExpectedOutcomes, extractDid, extractMethodName, getWorkingMethods, getWorkingUrls } = require("./utils"); 9 | 10 | const testDataSkeleton = { 11 | implementation: 'Universal Resolver', 12 | implementer: 'Decentralized Identity Foundation and Contributors', 13 | didMethod: '', 14 | expectedOutcomes: { 15 | defaultOutcomes: [], 16 | invalidDidErrorOutcome: [], 17 | notFoundErrorOutcome: [], 18 | representationNotSupportedErrorOutcome: [], 19 | deactivatedOutcome: [] 20 | }, 21 | executions: [] 22 | } 23 | 24 | const getOutputPath = () => { 25 | return argv["OUTPUT_PATH"] !== undefined ? argv["OUTPUT_PATH"] : './' 26 | } 27 | 28 | /* Set mode of test results 29 | * LOCAL => local files are created for manual upload 30 | * SERVER => tests are run against a hosted testserver 31 | * */ 32 | const getMode = () => { 33 | if (argv["MODE"] !== undefined) { 34 | return argv["MODE"].toUpperCase(); 35 | } else { 36 | return "SERVER"; 37 | } 38 | } 39 | 40 | const getTestset = () => { 41 | if (argv["DRIVER_STATUS_REPORT"] !== undefined) { 42 | return argv["DRIVER_STATUS_REPORT"]; 43 | } else { 44 | return core.getInput('DRIVER_STATUS_REPORT'); 45 | } 46 | } 47 | 48 | const getHost = () => { 49 | if (argv["HOST"] !== undefined) { 50 | return argv["HOST"]; 51 | } else { 52 | return core.getInput('host'); 53 | } 54 | } 55 | 56 | const getShouldGenerateDefaultFile = () => { 57 | if (argv["GENERATE_DEFAULT_FILE"] !== undefined) { 58 | return JSON.parse(argv["GENERATE_DEFAULT_FILE"].toLowerCase()) 59 | } else { 60 | return false; 61 | } 62 | } 63 | 64 | try { 65 | // Get the JSON webhook payload for the event that triggered the workflow 66 | const payload = JSON.stringify(github.context.payload, undefined, 2) 67 | console.log(`The event payload: ${payload}`); 68 | 69 | // Input variables 70 | const mode = getMode(); 71 | console.log(`Running in ${mode} mode`); 72 | 73 | // LOCAL mode env variables 74 | const outputPath = getOutputPath(); 75 | console.log(`Output path for testfiles ${outputPath}`); 76 | const shouldGenerateDefaultFile = getShouldGenerateDefaultFile(); 77 | 78 | // SERVER mode env variables 79 | const host = getHost(); 80 | console.log(`Testserver host ${host}`); 81 | 82 | // Common testset 83 | const testSet = getTestset(); 84 | console.log(`Running with testSet: ${testSet}`); 85 | 86 | const rawData = fs.readFileSync(testSet); 87 | const resolutionResults = JSON.parse(rawData); 88 | 89 | const workingMethods = getWorkingMethods(resolutionResults); 90 | console.log('Working methods', workingMethods); 91 | 92 | const workingUrls = getWorkingUrls(resolutionResults); 93 | console.log('Working Urls', workingUrls); 94 | 95 | const resolvers = []; 96 | 97 | workingMethods.forEach(workingMethodName => { 98 | const testData = {...testDataSkeleton}; 99 | resetTestData(testData); 100 | testData.didMethod = `did:${workingMethodName}`; 101 | 102 | let index = 0; 103 | workingUrls.forEach(url => { 104 | if (workingMethodName === extractMethodName(extractDid(url))) { 105 | createExpectedOutcomes(testData, resolutionResults[url], index) 106 | 107 | testData.executions.push({ 108 | function: 'resolveRepresentation', 109 | input: { 110 | did: resolutionResults[url].resolutionResponse["application/did+ld+json"].didDocument.id, 111 | resolutionOptions: { 112 | accept: "application/did+ld+json" 113 | } 114 | }, 115 | output: { 116 | didResolutionMetadata: resolutionResults[url].resolutionResponse["application/did+ld+json"].didResolutionMetadata, 117 | didDocumentStream: JSON.stringify(resolutionResults[url].resolutionResponse["application/did+ld+json"].didDocument), 118 | didDocumentMetadata: resolutionResults[url].resolutionResponse["application/did+ld+json"].didDocumentMetadata 119 | } 120 | }) 121 | index++; 122 | } 123 | }); 124 | 125 | if (mode === "LOCAL") generateLocalFile(testData, workingMethodName, outputPath); 126 | if (mode === "SERVER") resolvers.push(testData); 127 | }) 128 | 129 | if (mode === "LOCAL" && shouldGenerateDefaultFile === true) { 130 | generateDefaultFile(outputPath) 131 | } 132 | 133 | if (mode === "SERVER") { 134 | runTests(resolvers, host, outputPath); 135 | } 136 | } catch (error) { 137 | core.setFailed(error.message); 138 | } -------------------------------------------------------------------------------- /ci/run-did-test-suite/app/local-files-utils.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs") 2 | 3 | const generateLocalFile = (testData, methodName, path) => { 4 | fs.writeFileSync( 5 | `${path}/universal-resolver-did-${methodName}.json`, 6 | JSON.stringify(testData, null, 4) 7 | ); 8 | }; 9 | 10 | const generateDefaultFile = (path) => { 11 | fs.readdir(path, (err, files) => { 12 | 13 | const resolvers = [] 14 | 15 | files.forEach(file => { 16 | console.log(file); 17 | const fileContent = JSON.parse(fs.readFileSync(`${path}/${file}`)); 18 | console.log(fileContent); 19 | if (file.startsWith('universal-resolver') || file.startsWith('resolver')) { 20 | resolvers.push(`require('../implementations/${file}')`) 21 | } 22 | }); 23 | console.log(resolvers) 24 | 25 | fs.writeFileSync( 26 | '/Users/devfox/testsuites/did-test-suite/packages/did-core-test-server/suites/did-resolution/default.js', 27 | `module.exports = { 28 | name: '7.1 DID Resolution', 29 | resolvers: [${resolvers}] 30 | }` 31 | ); 32 | }); 33 | }; 34 | 35 | module.exports = { 36 | generateDefaultFile, 37 | generateLocalFile 38 | } 39 | -------------------------------------------------------------------------------- /ci/run-did-test-suite/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "run-did-test-suite", 3 | "version": "1.0.0", 4 | "description": "Run did-test-suite action", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Bernhard Fuchs", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@actions/core": "^1.2.7", 13 | "@actions/github": "^4.0.0", 14 | "axios": "^0.21.2", 15 | "dayjs": "^1.10.4", 16 | "minimist": "^1.2.6" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ci/run-did-test-suite/app/testserver-utils.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const fs = require('fs'); 3 | 4 | const runTests = (resolvers, host, outputPath) => { 5 | axios.post(host, 6 | { 7 | suites: [ 8 | { 9 | suite_name: 'did-resolution', 10 | resolvers: resolvers 11 | } 12 | ] 13 | } 14 | ).then(res => { 15 | const timestamp = new Date().toISOString().split('.')[0]; 16 | fs.writeFileSync(`${outputPath}/did-test-suite-report-${timestamp}.json`, JSON.stringify(res.data.suitesReportJson, null, 2)) 17 | }).catch(err => console.log(err)); 18 | } 19 | 20 | module.exports = {runTests} 21 | -------------------------------------------------------------------------------- /ci/run-did-test-suite/app/utils.js: -------------------------------------------------------------------------------- 1 | const extractDid = (url) => { 2 | const didWithParams = url.split('/'); 3 | return didWithParams[didWithParams.length - 1].split('?')[0]; 4 | } 5 | 6 | const extractMethodName = (url) => { 7 | return extractDid(url).split(':')[1]; 8 | } 9 | 10 | const getWorkingMethods = (resolutionResults) => { 11 | const workingMethods = []; 12 | const urls = Object.keys( resolutionResults ); 13 | urls.forEach(url => { 14 | if (resolutionResults[url].status === 200 && resolutionResults[url].resolutionResponse["application/did+ld+json"].didDocument.id !== undefined) { 15 | workingMethods.push(extractMethodName(url)); 16 | } 17 | }) 18 | return Array.from(new Set(workingMethods)) 19 | } 20 | 21 | const getWorkingUrls = (resolutionResults) => { 22 | const workingUrls = []; 23 | const urls = Object.keys( resolutionResults ); 24 | urls.forEach(url => { 25 | if (resolutionResults[url].status === 200 && resolutionResults[url].resolutionResponse["application/did+ld+json"].didDocument.id !== undefined) { 26 | workingUrls.push(url); 27 | } 28 | }) 29 | return Array.from(new Set(workingUrls)) 30 | } 31 | 32 | const createExpectedOutcomes = (testData, resolutionResult, index) => { 33 | 34 | if (resolutionResult.resolutionResponse["application/did+ld+json"].didDocumentMetadata.deactivated === true) { 35 | testData.expectedOutcomes.deactivatedOutcome[0] === undefined ? 36 | testData.expectedOutcomes.deactivatedOutcome[0] = index : 37 | testData.expectedOutcomes.deactivatedOutcome.push(index) 38 | } else { 39 | testData.expectedOutcomes.defaultOutcomes[0] === undefined ? 40 | testData.expectedOutcomes.defaultOutcomes[0] = index : 41 | testData.expectedOutcomes.defaultOutcomes.push(index) 42 | } 43 | } 44 | 45 | const resetTestData = (testData) => { 46 | testData.executions = []; 47 | testData.expectedOutcomes.defaultOutcomes = []; 48 | testData.expectedOutcomes.invalidDidErrorOutcome = []; 49 | testData.expectedOutcomes.notFoundErrorOutcome = []; 50 | testData.expectedOutcomes.representationNotSupportedErrorOutcome = []; 51 | testData.expectedOutcomes.deactivatedOutcome = []; 52 | } 53 | 54 | module.exports = { 55 | extractDid, 56 | extractMethodName, 57 | getWorkingMethods, 58 | getWorkingUrls, 59 | createExpectedOutcomes, 60 | resetTestData 61 | } -------------------------------------------------------------------------------- /ci/run-did-test-suite/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "#### Run did-test-suite ####" 4 | 5 | echo "Running with parameters:" 6 | sh -c "echo $*" 7 | 8 | echo "host: $INPUT_HOST" 9 | echo "driver_status_report: $INPUT_DRIVER_STATUS_REPORT" 10 | 11 | node /run-did-test-suite/index.js --HOST="$INPUT_HOST" --DRIVER_STATUS_REPORT="$INPUT_DRIVER_STATUS_REPORT" --OUTPUT_PATH="$INPUT_REPORTS_FOLDER" 12 | 13 | echo "Push report file to repo" 14 | git status 15 | git config --global user.email "admin@danubetech.com" 16 | git config --global user.name "Get driver status workflow" 17 | git fetch && git pull --ff-only 18 | git add . && git commit -m "did-test-suite report" && git push -------------------------------------------------------------------------------- /docs/branching-strategy.md: -------------------------------------------------------------------------------- 1 | # Universal Resolver — Branching Strategy 2 | 3 | The goals for our branching strategy are: 4 | * The `main` branch should be deployable at any time. This implies a stable build and the assurance that the core-functionality is provided at any time when cloning the `main` branch. 5 | * The `main` branch should stay active. As collaboratively working with multiple developers, we encourage merging the code as frequently as possible. This will potentially disclose issues at an early stage and facilitate the repair. Furthermore, it makes clear that the `main` branch is the preferred choice that newcomers want to clone. 6 | * In order not to waste time the branching strategy should stay as simple as possible. 7 | 8 | Among a bunch of various strategies we have chosen *GitHub Flow*, which is a lightweight branching-strategy encouraged by the GitHub dev-teams. Details can be found here: https://guides.github.com/introduction/flow/ 9 | The following recipe shortly describes the typical steps that developers need to be take into account when updating the code base: 10 | 11 | ![](https://hackernoon.com/hn-images/1*iHPPa72N11sBI_JSDEGxEA.png) 12 | 13 | 1. ***Create a branch***: When implementing a new feature, some improvement or a bugfix, a new branch should be created of the `main` branch. In order to preserve an organized git-repo, we agreed on following prefixes for branches: 14 | 15 | - feature-\ -> adding/changing functionality, whereas the branch-name should reflect the intention 16 | - bugfix-\ -> fixing Github-issues 17 | - refactor-\ -> cleanup, maintanance, improving code quality, adding Unit tests 18 | - ci-\ -> anything related to continous integration 19 | - docs-\ -> updating/extending documentation, no code changes allowed 20 | - release-\ -> release of a new major version 21 | 22 | Feature branches are the most inclusive, which can contain refactoring and documentation. 23 | Always use the GitHub-issue name as part of the branch-name, if there is a corresponding issue available. 24 | 25 | 2. ***Commit some code***: Add your changes to the new branch and commit regularly with a descriptive commit-message. This builds up a transparent history of work and makes a roll back of changes easier. 26 | 3. ***Open a Pull Request (PR)***: Once your changes are complete (or you want some feedback at an early stage of development) open a PR against the `main` branch. A PR initiates a discussion with the maintainer, which will review your code at this point. Furthermore, the [[CI/CD process|Continuous-Integration-and-Delivery]] will be kicked off and your code will be deployed to the dev-system. 27 | 4. ***Discuss, review code and deployment***: Wait for feedback of the maintainer and check the deployment at the dev-system. In case of contributing a new driver the maintainer will also add the deployment-scripts in the scope of this PR. You may also be requested to make some changes to your code. Finally, the new changes should be safely incorporated to the `main` branch and the updated Universal Resolver is running smoothly in dev-environment. 28 | 5. ***Merge to the `main` branch***: If all parties involved in the discussion are satisfied, the maintainer will merge the PR into the `main` branch and will close the PR itself. You are free to delete your branch as all changes have already been incorporated in the `main` branch. 29 | -------------------------------------------------------------------------------- /docs/continuous-integration-and-delivery.md: -------------------------------------------------------------------------------- 1 | # Universal Resolver — Continuous Integration and Delivery 2 | 3 | This section describes the building-blocks and ideas of the implemented CI/CD pipeline. In case of issues or requests in the scope of CI/CD, please directly consult the maintainers of the repository. 4 | 5 | ## Intro 6 | 7 | The CI/CD pipeline helps achieving the following goals: 8 | * Detection of problems asap 9 | * Short and robust release cycles 10 | * Avoidance of repetitive, manual tasks 11 | * Increase of Software Quality 12 | 13 | After every code change the CI/CD pipeline builds all software packages/Docker containers automatically. Once the containers are built, they are automatically deployed to the dev-system. By these measures building as well as deployment issues are immediately discovered. Once the freshly built containers are deployed, automatic tests are run in order to verify the software and to detect functional issues. 14 | 15 | ## Building Blocks 16 | 17 | The CI/CD pipeline is constructed by using GitHub Actions. The workflows are run after every push to the `main` branch and on ever PR against the `main` branch. 18 | The workflows consist of several steps. Currently, the two main steps are: 19 | 20 | 1. Building the resolver (workflow file https://github.com/decentralized-identity/universal-resolver/blob/main/.github/workflows/docker-build-and-deploy-image.yml) 21 | 2. Deploying the resolver (workflow file https://github.com/decentralized-identity/universal-resolver/blob/main/.github/workflows/kubernetes-deploy-to-cluster.yml) 22 | 23 | The first step builds a Docker image and pushes it to Docker Hub at https://hub.docker.com/u/universalresolver. 24 | The second step takes the image and deploys it (create or update) to the configured Kubernetes cluster. 25 | 26 | ## Steps of the CI/CD Workflow 27 | 28 | ![](https://user-images.githubusercontent.com/55081379/68245944-2a78db00-0018-11ea-8ebe-22c19d5ad096.PNG) 29 | 30 | 1. Dev pushes code to GitHub 31 | 2. GitHub Actions (GHA) is triggered by the „push“ event, clones the repo and runs the workflow for every container: 32 | * (a) Docker build 33 | * (b) Docker push (Docker image goes to DockerHub) 34 | * (c) Deploy to Kubernetes 35 | * (d) Runs a test-container 36 | 3. Manual and automated feedback 37 | 38 | ## Open Issues regarding CI/CD 39 | 40 | * Make all drivers accessible via sub-domains eg. elem.dev.uniresolver.io 41 | * Bundle new release for resolver.identity.foundation (only working drivers) 42 | * Render Smoke Test results as HTML-page and host it via gh-pages 43 | * Update documentation 44 | -------------------------------------------------------------------------------- /docs/design-goals.md: -------------------------------------------------------------------------------- 1 | # Universal Resolver — Design Goals 2 | 3 | The main design objectives of the UR are: 4 | 5 | * **Generic:** The UR will be a neutral implementation that will be usable as broadly as possible, i.e. not limited towards a specific use case or higher-level protocol. 6 | * **Simple:** The most common uses of the UR should be very simple to execute (e.g. "give me the DDO or the Hub endpoint for this identifier"). 7 | * **Extensible:** The UR may ship by default with a few key implementations of common identifier systems, but must also be extensible to easily add new ones through a plugin mechanism. 8 | 9 | The following functionality is out of scope for the UR: 10 | 11 | * The UR will not address registration or modification of identifiers and associated data. 12 | * The UR will not involve authentication or authorization of the UR user, i.e. all its functionality will be publicly usable by anyone. [debatable?] 13 | * The UR will not implement any functionality that builds on top of resolution, such as the DIF Hub protocols, Blockstack storage layer, XDI, DID Auth, DKMS, etc. [debatable?], but it will be able to serve as a building block for such higher-level protocols. 14 | -------------------------------------------------------------------------------- /docs/dev-system.md: -------------------------------------------------------------------------------- 1 | # Universal Resolver — Development System 2 | 3 | The development instance, which runs the latest code-base of the Universal Resolver project, is hosted at: 4 | 5 | https://dev.uniresolver.io 6 | 7 | DIDs can be resolved by calling the resolver: 8 | 9 | https://dev.uniresolver.io/1.0/identifiers/did:btcr:xz35-jznz-q6mr-7q6 10 | 11 | 12 | The software is automatically updated on every commit and PR on the master branch. See [CI-CD](/docs/continuous-integration-and-delivery.md) for more details 13 | 14 | Currently the system is deployed in the AWS cloud by the use of the Elastic Kubernetes Service (EKS). Please be aware that the use of AWS is not mandatory for hosting the resolver. Any environment that supports Docker Compose or Kubernetes will be capable of running an instance of the Universal Resolver. 15 | 16 | We are using two `m5.large` instances (2 vCPU / 8GB RAM) due to the limitations of 29 pods per instance of this type on AWS EKS. This should not be treated as a recommendation, just an information. 17 | 18 | ## AWS Architecture 19 | 20 | This picture illustrates the AWS architecture for hosting the Universal resolver as well as the traffic-flow through the system. 21 | 22 |

23 | 24 | The entry-point to the system is the public Internet facing Application Load Balancer (ALB), that sits at the edge of the AWS cloud and is bound to the DNS name “dev.uniresolver.io”. When resolving DIDs the traffic flows through the ALB to the resolver. Based on the configuration of each DID-method the resolver calls the corresponding DID-driver (typical scenario) or may call another HTTP endpoint (another resolver or directly the DLT if HTTP is supported). In order to gain performance, blockchain nodes may also be added to the deployment, as sketched at Driver C. 25 | 26 | The Kubernetes cluster is spanned across multiple Availability Zones (AZ), which are essential parts for providing fault-tolerance and achieving a high-availability HA of the system. This means that no downtime is to be expected in case of failing parts of the system, as the healthy parts will take over operations reliably. 27 | 28 | If containers, like Universal Resolver drivers, are added or removed, the ALB ingress controller https://kubernetes-sigs.github.io/aws-alb-ingress-controller/ takes care of notifying the ALB. Due to this mechanism the ALB stays aware of the system state and is able to keep traffic-routes healthy. 29 | 30 | Further details regarding the automated system-update are described at [CI-CD](/docs/continuous-integration-and-delivery.md). 31 | -------------------------------------------------------------------------------- /docs/figures/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentralized-identity/universal-resolver/6ea1e63db7e2ca1a13eca0763a7d2270d63eeae6/docs/figures/architecture.png -------------------------------------------------------------------------------- /docs/figures/aws-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentralized-identity/universal-resolver/6ea1e63db7e2ca1a13eca0763a7d2270d63eeae6/docs/figures/aws-architecture.png -------------------------------------------------------------------------------- /docs/figures/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentralized-identity/universal-resolver/6ea1e63db7e2ca1a13eca0763a7d2270d63eeae6/docs/figures/overview.png -------------------------------------------------------------------------------- /docs/figures/protocol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentralized-identity/universal-resolver/6ea1e63db7e2ca1a13eca0763a7d2270d63eeae6/docs/figures/protocol.png -------------------------------------------------------------------------------- /docs/java-components.md: -------------------------------------------------------------------------------- 1 | # Universal Resolver — Java Components 2 | 3 | This is a Java implementation of a Universal Resolver. See [universal-resolver](https://github.com/decentralized-identity/universal-resolver/) for a general introduction to Universal Resolvers and drivers. 4 | 5 | ## Build (native Java) 6 | 7 | Maven build: 8 | 9 | mvn clean install 10 | 11 | ## Local Resolver 12 | 13 | You can use a [Local Resolver](https://github.com/decentralized-identity/universal-resolver/tree/main/uni-resolver-client) in your Java project that invokes drivers locally (either directly via their JAVA API or via a Docker REST API). 14 | 15 | Dependency: 16 | 17 | 18 | decentralized-identity 19 | uni-resolver-local 20 | 0.1-SNAPSHOT 21 | 22 | 23 | [Example Use](https://github.com/decentralized-identity/universal-resolver/blob/main/examples/src/main/java/uniresolver/examples/TestLocalUniResolver.java): 24 | 25 | ## Web Resolver 26 | 27 | You can deploy a [Web Resolver](https://github.com/decentralized-identity/universal-resolver/tree/main/uni-resolver-web) that can be called by clients and invokes drivers locally (either directly via their JAVA API or via a Docker REST API). 28 | 29 | See the [Example Configuration](https://github.com/decentralized-identity/universal-resolver/blob/main/uni-resolver-web/src/main/webapp/WEB-INF/applicationContext.xml). 30 | 31 | How to run: 32 | 33 | mvn jetty:run 34 | 35 | ## Client Resolver 36 | 37 | You can use a [Client Resolver](https://github.com/decentralized-identity/universal-resolver/tree/main/uni-resolver-client) in your Java project that calls a remote Web Resolver. 38 | 39 | Dependency: 40 | 41 | 42 | decentralized-identity 43 | uni-resolver-client 44 | 0.1-SNAPSHOT 45 | 46 | 47 | [Example Use](https://github.com/decentralized-identity/universal-resolver/blob/main/examples/src/main/java/uniresolver/examples/TestClientUniResolver.java): 48 | 49 | ## About 50 | 51 | Decentralized Identity Foundation - http://identity.foundation/ 52 | -------------------------------------------------------------------------------- /docs/logo-dif.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentralized-identity/universal-resolver/6ea1e63db7e2ca1a13eca0763a7d2270d63eeae6/docs/logo-dif.png -------------------------------------------------------------------------------- /docs/logo-ngi0pet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentralized-identity/universal-resolver/6ea1e63db7e2ca1a13eca0763a7d2270d63eeae6/docs/logo-ngi0pet.png -------------------------------------------------------------------------------- /docs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Universal Resolver — Troubleshooting 2 | 3 | If docker-compose complains about wrong versions then you probably have a too old docker-compose version. 4 | 5 | On Ubuntu 16.04 remove docker-compose and install a new version e.g. 6 | ``` 7 | sudo apt-get remove docker-compose 8 | curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose 9 | chmod +x /usr/local/bin/docker-compose 10 | ``` 11 | You might want to adjust the version number 1.22.0 to the latest one. Please see: [Installing docker-compose](https://docs.docker.com/compose/install/#install-compose) 12 | 13 | -------------------------------------------------------------------------------- /driver-http/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /driver-http/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | uni-resolver-driver-http 5 | jar 6 | uni-resolver-driver-http 7 | 8 | 9 | decentralized-identity 10 | uni-resolver 11 | 0.33-SNAPSHOT 12 | 13 | 14 | 15 | 16 | decentralized-identity 17 | uni-resolver-driver 18 | 19 | 20 | org.apache.httpcomponents 21 | httpclient 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /driver/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /driver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | uni-resolver-driver 5 | jar 6 | uni-resolver-driver 7 | 8 | 9 | decentralized-identity 10 | uni-resolver 11 | 0.33-SNAPSHOT 12 | 13 | 14 | 15 | 6.1.0 16 | 6.2.3 17 | 18 | 19 | 20 | 21 | 22 | jakarta.servlet 23 | jakarta.servlet-api 24 | ${jakarta.servlet-api.version} 25 | 26 | 27 | org.springframework 28 | spring-web 29 | ${org.springframework-spring-web.version} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | jakarta.servlet 38 | jakarta.servlet-api 39 | provided 40 | 41 | 42 | decentralized-identity 43 | uni-resolver-core 44 | 45 | 46 | org.springframework 47 | spring-web 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /driver/src/main/java/uniresolver/driver/AbstractDriver.java: -------------------------------------------------------------------------------- 1 | package uniresolver.driver; 2 | 3 | public abstract class AbstractDriver implements Driver { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /driver/src/main/java/uniresolver/driver/Driver.java: -------------------------------------------------------------------------------- 1 | package uniresolver.driver; 2 | 3 | import foundation.identity.did.DID; 4 | import foundation.identity.did.DIDURL; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import uniresolver.DereferencingException; 8 | import uniresolver.ResolutionException; 9 | import uniresolver.result.DereferenceResult; 10 | import uniresolver.result.ResolveResult; 11 | 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | public interface Driver { 17 | 18 | public static final String PROPERTIES_MEDIA_TYPE = "application/json"; 19 | public static final String TEST_IDENTIFIER_MEDIA_TYPE = "application/json"; 20 | public static final String TRAITS_MEDIA_TYPE = "application/json"; 21 | 22 | static final Logger log = LoggerFactory.getLogger(Driver.class); 23 | 24 | public ResolveResult resolve(DID did, Map resolutionOptions) throws ResolutionException; 25 | 26 | public DereferenceResult dereference(DIDURL didUrl, Map dereferenceOptions) throws DereferencingException, ResolutionException; 27 | 28 | default public Map properties() throws ResolutionException { 29 | return Collections.emptyMap(); 30 | } 31 | 32 | default public List testIdentifiers() throws ResolutionException { 33 | return Collections.emptyList(); 34 | } 35 | 36 | default public Map traits() throws ResolutionException { 37 | return Collections.emptyMap(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /driver/src/main/java/uniresolver/driver/servlet/InitServlet.java: -------------------------------------------------------------------------------- 1 | package uniresolver.driver.servlet; 2 | 3 | import jakarta.servlet.Servlet; 4 | import jakarta.servlet.ServletConfig; 5 | import jakarta.servlet.ServletException; 6 | import jakarta.servlet.http.HttpServlet; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import uniresolver.driver.Driver; 10 | 11 | import java.lang.reflect.InvocationTargetException; 12 | 13 | public class InitServlet extends HttpServlet implements Servlet { 14 | 15 | private static final Logger log = LoggerFactory.getLogger(InitServlet.class); 16 | 17 | private static Driver driver = null; 18 | 19 | public InitServlet() { 20 | 21 | super(); 22 | } 23 | 24 | @SuppressWarnings("unchecked") 25 | @Override 26 | public void init(ServletConfig config) throws ServletException { 27 | 28 | super.init(config); 29 | 30 | if (driver == null) { 31 | 32 | String driverClassName = config.getInitParameter("Driver"); 33 | Class driverClass; 34 | 35 | try { 36 | 37 | driverClass = driverClassName == null ? null : (Class) Class.forName(driverClassName); 38 | driver = driverClass == null ? null : driverClass.getConstructor().newInstance(); 39 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) { 40 | 41 | throw new ServletException(ex.getMessage(), ex); 42 | } 43 | 44 | if (driver == null) throw new ServletException("Unable to load driver: (no 'Driver' init parameter)"); 45 | 46 | if (log.isInfoEnabled()) log.info("Loaded driver: " + driverClass); 47 | } 48 | } 49 | 50 | public static Driver getDriver() { 51 | return InitServlet.driver; 52 | } 53 | 54 | public static void setDriver(Driver driver) { 55 | InitServlet.driver = driver; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /driver/src/main/java/uniresolver/driver/servlet/PropertiesServlet.java: -------------------------------------------------------------------------------- 1 | package uniresolver.driver.servlet; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import jakarta.servlet.Servlet; 5 | import jakarta.servlet.http.HttpServlet; 6 | import jakarta.servlet.http.HttpServletRequest; 7 | import jakarta.servlet.http.HttpServletResponse; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import uniresolver.driver.Driver; 11 | 12 | import java.io.IOException; 13 | import java.util.Map; 14 | 15 | public class PropertiesServlet extends HttpServlet implements Servlet { 16 | 17 | private static final Logger log = LoggerFactory.getLogger(PropertiesServlet.class); 18 | 19 | private static final ObjectMapper objectMapper = new ObjectMapper(); 20 | 21 | public PropertiesServlet() { 22 | super(); 23 | } 24 | 25 | @Override 26 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 27 | 28 | // read request 29 | 30 | request.setCharacterEncoding("UTF-8"); 31 | response.setCharacterEncoding("UTF-8"); 32 | 33 | if (log.isInfoEnabled()) log.info("Incoming request."); 34 | 35 | // get properties 36 | 37 | Map properties; 38 | String propertiesString; 39 | 40 | try { 41 | properties = InitServlet.getDriver() == null ? null : InitServlet.getDriver().properties(); 42 | propertiesString = properties == null ? null : objectMapper.writeValueAsString(properties); 43 | } catch (Exception ex) { 44 | if (log.isWarnEnabled()) log.warn("Properties problem: " + ex.getMessage(), ex); 45 | ServletUtil.sendResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Properties problem: " + ex.getMessage()); 46 | return; 47 | } 48 | 49 | if (log.isInfoEnabled()) log.info("Properties: " + properties); 50 | 51 | // no properties? 52 | 53 | if (properties == null) { 54 | ServletUtil.sendResponse(response, HttpServletResponse.SC_NOT_FOUND, "No properties."); 55 | return; 56 | } 57 | 58 | // write properties 59 | 60 | ServletUtil.sendResponse(response, HttpServletResponse.SC_OK, Driver.PROPERTIES_MEDIA_TYPE, propertiesString); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /driver/src/main/java/uniresolver/driver/servlet/ServletUtil.java: -------------------------------------------------------------------------------- 1 | package uniresolver.driver.servlet; 2 | 3 | import jakarta.servlet.http.HttpServletResponse; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.io.PrintWriter; 10 | import java.util.Map; 11 | 12 | public class ServletUtil { 13 | 14 | private static final Logger log = LoggerFactory.getLogger(ServletUtil.class); 15 | 16 | static void sendResponse(HttpServletResponse response, int status, Map headers, Object body) throws IOException { 17 | 18 | if (log.isDebugEnabled()) log.debug("Sending response with status " + status + " and headers " + headers + " and body (" + (body == null ? null : body.getClass()) + ")"); 19 | 20 | response.setStatus(status); 21 | if (headers != null) for (Map.Entry header : headers.entrySet()) response.setHeader(header.getKey(), header.getValue()); 22 | response.setHeader("Access-Control-Allow-Origin", "*"); 23 | 24 | if (body instanceof String) { 25 | 26 | PrintWriter printWriter = response.getWriter(); 27 | printWriter.write((String) body); 28 | printWriter.flush(); 29 | printWriter.close(); 30 | } else if (body instanceof byte[]) { 31 | 32 | OutputStream outputStream = response.getOutputStream(); 33 | outputStream.write((byte[]) body); 34 | outputStream.flush(); 35 | outputStream.close(); 36 | } else if (body != null) { 37 | 38 | PrintWriter printWriter = response.getWriter(); 39 | printWriter.write(body.toString()); 40 | printWriter.flush(); 41 | printWriter.close(); 42 | } 43 | 44 | response.flushBuffer(); 45 | } 46 | 47 | static void sendResponse(HttpServletResponse response, int status, Map headers) throws IOException { 48 | 49 | sendResponse(response, status, headers, null); 50 | } 51 | 52 | static void sendResponse(HttpServletResponse response, int status, String contentType, Object body) throws IOException { 53 | 54 | sendResponse(response, status, Map.of("Content-Type", contentType), body); 55 | } 56 | 57 | static void sendResponse(HttpServletResponse response, int status, Object body) throws IOException { 58 | 59 | sendResponse(response, status, (Map) null, body); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /driver/src/main/java/uniresolver/driver/util/MediaTypeUtil.java: -------------------------------------------------------------------------------- 1 | package uniresolver.driver.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.http.MediaType; 6 | 7 | import java.util.List; 8 | import java.util.Objects; 9 | 10 | public class MediaTypeUtil { 11 | 12 | private static final Logger log = LoggerFactory.getLogger(MediaTypeUtil.class); 13 | 14 | public static boolean isMediaTypeAcceptable(MediaType acceptMediaType, String mediaTypeString) { 15 | if (mediaTypeString == null) throw new NullPointerException(); 16 | MediaType mediaType = MediaType.valueOf(mediaTypeString); 17 | boolean acceptable = false; 18 | if (acceptMediaType.includes(mediaType)) { 19 | acceptable = true; 20 | } 21 | if (acceptMediaType.getType().equals(mediaType.getType()) && mediaType.getSubtype().endsWith("+" + acceptMediaType.getSubtype())) { 22 | acceptable = true; 23 | } 24 | if (! MediaType.ALL.equals(acceptMediaType)) { 25 | if (acceptMediaType.getParameters() != null) { 26 | acceptable &= Objects.equals(acceptMediaType.getParameter("profile"), mediaType.getParameter("profile")); 27 | } 28 | } 29 | if (log.isDebugEnabled()) log.debug("Checking if media type " + mediaType + " is acceptable for " + acceptMediaType + ": " + acceptable); 30 | return acceptable; 31 | } 32 | 33 | public static boolean isMediaTypeAcceptable(List acceptMediaTypes, String mediaTypeString) { 34 | for (MediaType acceptMediaType : acceptMediaTypes) { 35 | if (isMediaTypeAcceptable(acceptMediaType, mediaTypeString)) { 36 | return true; 37 | } 38 | } 39 | return false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /examples/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | uni-resolver-examples 6 | jar 7 | uni-resolver-examples 8 | 9 | decentralized-identity 10 | uni-resolver 11 | 0.4-SNAPSHOT 12 | 13 | 14 | 15 | 16 | decentralized-identity 17 | uni-resolver-local 18 | compile 19 | 20 | 21 | decentralized-identity 22 | uni-resolver-client 23 | compile 24 | 25 | 26 | decentralized-identity 27 | uni-resolver-driver-did-sov 28 | 0.4-SNAPSHOT 29 | compile 30 | 31 | 32 | decentralized-identity 33 | uni-resolver-driver-did-btcr 34 | 0.1.3-SNAPSHOT 35 | compile 36 | 37 | 38 | org.apache.logging.log4j 39 | log4j-slf4j-impl 40 | 2.13.3 41 | compile 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/sovrin/danube.txn: -------------------------------------------------------------------------------- 1 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blskey":"4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba","blskey_pop":"RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1","client_ip":"92.42.139.24","client_port":9702,"node_ip":"92.42.139.24","node_port":9701,"services":["VALIDATOR"]},"dest":"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv"},"metadata":{"from":"Th7MpTaRZVRYnPiabds81Y"},"type":"0"},"txnMetadata":{"seqNo":1,"txnId":"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62"},"ver":"1"} 2 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node2","blskey":"37rAPpXVoxzKhz7d9gkUe52XuXryuLXoM6P6LbWDB7LSbG62Lsb33sfG7zqS8TK1MXwuCHj1FKNzVpsnafmqLG1vXN88rt38mNFs9TENzm4QHdBzsvCuoBnPH7rpYYDo9DZNJePaDvRvqJKByCabubJz3XXKbEeshzpz4Ma5QYpJqjk","blskey_pop":"Qr658mWZ2YC8JXGXwMDQTzuZCWF7NK9EwxphGmcBvCh6ybUuLxbG65nsX4JvD4SPNtkJ2w9ug1yLTj6fgmuDg41TgECXjLCij3RMsV8CwewBVgVN67wsA45DFWvqvLtu4rjNnE9JbdFTc1Z4WCPA3Xan44K1HoHAq9EVeaRYs8zoF5","client_ip":"92.42.139.24","client_port":9704,"node_ip":"92.42.139.24","node_port":9703,"services":["VALIDATOR"]},"dest":"8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb"},"metadata":{"from":"EbP4aYNeTHL6q385GuVpRV"},"type":"0"},"txnMetadata":{"seqNo":2,"txnId":"1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc"},"ver":"1"} 3 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"92.42.139.24","client_port":9706,"node_ip":"92.42.139.24","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"},"metadata":{"from":"4cU41vWW82ArfxJxHkzXPG"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"},"ver":"1"} 4 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"92.42.139.24","client_port":9708,"node_ip":"92.42.139.24","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"} 5 | -------------------------------------------------------------------------------- /examples/sovrin/lib/.gitignore: -------------------------------------------------------------------------------- 1 | libindy.so 2 | 3 | /.idea/ 4 | *.iml 5 | -------------------------------------------------------------------------------- /examples/sovrin/localhost.txn: -------------------------------------------------------------------------------- 1 | {"data":{"alias":"Node1","client_ip":"10.0.0.2","client_port":9702,"node_ip":"10.0.0.2","node_port":9701,"services":["VALIDATOR"]},"dest":"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv","identifier":"FYmoFw55GeQH7SRFa37dkx1d2dZ3zUF8ckg7wmL7ofN4","txnId":"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62","type":"0"} 2 | {"data":{"alias":"Node2","client_ip":"10.0.0.2","client_port":9704,"node_ip":"10.0.0.2","node_port":9703,"services":["VALIDATOR"]},"dest":"8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb","identifier":"8QhFxKxyaFsJy4CyxeYX34dFH8oWqyBv1P4HLQCsoeLy","txnId":"1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc","type":"0"} 3 | {"data":{"alias":"Node3","client_ip":"10.0.0.2","client_port":9706,"node_ip":"10.0.0.2","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya","identifier":"2yAeV5ftuasWNgQwVYzeHeTuM7LwwNtPR3Zg9N4JiDgF","txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4","type":"0"} 4 | {"data":{"alias":"Node4","client_ip":"10.0.0.2","client_port":9708,"node_ip":"10.0.0.2","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA","identifier":"FTE95CVthRtrBnK2PYCBbC9LghTcGwi9Zfi1Gz2dnyNx","txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008","type":"0"} 5 | -------------------------------------------------------------------------------- /examples/sovrin/mainnet.txn: -------------------------------------------------------------------------------- 1 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"ev1","client_ip":"54.207.36.81","client_port":"9702","node_ip":"18.231.96.215","node_port":"9701","services":["VALIDATOR"]},"dest":"GWgp6huggos5HrzHVDy5xeBkYHxPvrRZzjPNAyJAqpjA"},"metadata":{"from":"J4N1K1SEB8uY2muwmecY5q"},"type":"0"},"txnMetadata":{"seqNo":1,"txnId":"b0c82a3ade3497964cb8034be915da179459287823d92b5717e6d642784c50e6"},"ver":"1"} 2 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"zaValidator","client_ip":"154.0.164.39","client_port":"9702","node_ip":"154.0.164.39","node_port":"9701","services":["VALIDATOR"]},"dest":"BnubzSjE3dDVakR77yuJAuDdNajBdsh71ZtWePKhZTWe"},"metadata":{"from":"UoFyxT8BAqotbkhiehxHCn"},"type":"0"},"txnMetadata":{"seqNo":2,"txnId":"d5f775f65e44af60ff69cfbcf4f081cd31a218bf16a941d949339dadd55024d0"},"ver":"1"} 3 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"danube","client_ip":"128.130.204.35","client_port":"9722","node_ip":"128.130.204.35","node_port":"9721","services":["VALIDATOR"]},"dest":"476kwEjDj5rxH5ZcmTtgnWqDbAnYJAGGMgX7Sq183VED"},"metadata":{"from":"BrYDA5NubejDVHkCYBbpY5"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"ebf340b317c044d970fcd0ca018d8903726fa70c8d8854752cd65e29d443686c"},"ver":"1"} 4 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"royal_sovrin","client_ip":"35.167.133.255","client_port":"9702","node_ip":"35.167.133.255","node_port":"9701","services":["VALIDATOR"]},"dest":"Et6M1U7zXQksf7QM6Y61TtmXF1JU23nsHCwcp1M9S8Ly"},"metadata":{"from":"4ohadAwtb2kfqvXynfmfbq"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"24d391604c62e0e142ea51c6527481ae114722102e27f7878144d405d40df88d"},"ver":"1"} 5 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"digitalbazaar","client_ip":"34.226.105.29","client_port":"9701","node_ip":"34.226.105.29","node_port":"9700","services":["VALIDATOR"]},"dest":"D9oXgXC3b6ms3bXxrUu6KqR65TGhmC1eu7SUUanPoF71"},"metadata":{"from":"rckdVhnC5R5WvdtC83NQp"},"type":"0"},"txnMetadata":{"seqNo":5,"txnId":"56e1af48ef806615659304b1e5cf3ebf87050ad48e6310c5e8a8d9332ac5c0d8"},"ver":"1"} 6 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"OASFCU","client_ip":"38.70.17.248","client_port":"9702","node_ip":"38.70.17.248","node_port":"9701","services":["VALIDATOR"]},"dest":"8gM8NHpq2cE13rJYF33iDroEGiyU6wWLiU1jd2J4jSBz"},"metadata":{"from":"BFAeui85mkcuNeQQhZfqQY"},"type":"0"},"txnMetadata":{"seqNo":6,"txnId":"825aeaa33bc238449ec9bd58374b2b747a0b4859c5418da0ad201e928c3049ad"},"ver":"1"} 7 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"BIGAWSUSEAST1-001","client_ip":"34.224.255.108","client_port":"9796","node_ip":"34.224.255.108","node_port":"9769","services":["VALIDATOR"]},"dest":"HMJedzRbFkkuijvijASW2HZvQ93ooEVprxvNhqhCJUti"},"metadata":{"from":"L851TgZcjr6xqh4w6vYa34"},"type":"0"},"txnMetadata":{"seqNo":7,"txnId":"40fceb5fea4dbcadbd270be6d5752980e89692151baf77a6bb64c8ade42ac148"},"ver":"1"} 8 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"DustStorm","client_ip":"207.224.246.57","client_port":"9712","node_ip":"207.224.246.57","node_port":"9711","services":["VALIDATOR"]},"dest":"8gGDjbrn6wdq6CEjwoVStjQCEj3r7FCxKrA5d3qqXxjm"},"metadata":{"from":"FjuHvTjq76Pr9kdZiDadqq"},"type":"0"},"txnMetadata":{"seqNo":8,"txnId":"6d1ee3eb2057b8435333b23f271ab5c255a598193090452e9767f1edf1b4c72b"},"ver":"1"} 9 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"prosovitor","client_ip":"138.68.240.143","client_port":"9711","node_ip":"138.68.240.143","node_port":"9710","services":["VALIDATOR"]},"dest":"C8W35r9D2eubcrnAjyb4F3PC3vWQS1BHDg7UvDkvdV6Q"},"metadata":{"from":"Y1ENo59jsXYvTeP378hKWG"},"type":"0"},"txnMetadata":{"seqNo":9,"txnId":"15f22de8c95ef194f6448cfc03e93aeef199b9b1b7075c5ea13cfef71985bd83"},"ver":"1"} 10 | {"reqSignature":{},"txn":{"data":{"data":{"alias":"iRespond","client_ip":"52.187.10.28","client_port":"9702","node_ip":"52.187.10.28","node_port":"9701","services":["VALIDATOR"]},"dest":"3SD8yyJsK7iKYdesQjwuYbBGCPSs1Y9kYJizdwp2Q1zp"},"metadata":{"from":"JdJi97RRDH7Bx7khr1znAq"},"type":"0"},"txnMetadata":{"seqNo":10,"txnId":"b65ce086b631ed75722a4e1f28fc9cf6119b8bc695bbb77b7bdff53cfe0fc2e2"},"ver":"1"} 11 | -------------------------------------------------------------------------------- /examples/src/main/java/uniresolver/examples/TestClientUniResolver.java: -------------------------------------------------------------------------------- 1 | package uniresolver.examples; 2 | 3 | import uniresolver.client.ClientUniResolver; 4 | import uniresolver.result.ResolveDataModelResult; 5 | import uniresolver.result.ResolveRepresentationResult; 6 | import uniresolver.result.ResolveResult; 7 | 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class TestClientUniResolver { 13 | 14 | public static void main(String[] args) throws Exception { 15 | 16 | Map resolveOptions = new HashMap<>(); 17 | resolveOptions.put("accept", "application/did+ld+json"); 18 | 19 | ClientUniResolver uniResolver = new ClientUniResolver(); 20 | uniResolver.setResolveUri("http://localhost:8080/1.0/identifiers/"); 21 | 22 | ResolveRepresentationResult resolveRepresentationResult = uniResolver.resolveRepresentation("did:sov:WRfXPg8dantKVubE3HX8pw", resolveOptions); 23 | System.out.println(resolveRepresentationResult.toJson()); 24 | System.out.println(new String(resolveRepresentationResult.getDidDocumentStream(), StandardCharsets.UTF_8)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/src/main/java/uniresolver/examples/TestDriverDidBtcr.java: -------------------------------------------------------------------------------- 1 | package uniresolver.examples; 2 | import foundation.identity.did.DID; 3 | import info.weboftrust.btctxlookup.bitcoinconnection.BlockcypherAPIBitcoinConnection; 4 | import uniresolver.driver.did.btc1.DidBtcrDriver; 5 | import uniresolver.result.ResolveDataModelResult; 6 | import uniresolver.result.ResolveResult; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class TestDriverDidBtcr { 12 | 13 | public static void main(String[] args) throws Exception { 14 | 15 | Map driverProperties = new HashMap<>(); 16 | driverProperties.put("bitcoinConnection", "blockcypherapi"); 17 | DidBtcrDriver driver = new DidBtcrDriver(driverProperties); 18 | 19 | Map resolveOptions = new HashMap<>(); 20 | ResolveResult resolveResult = driver.resolve(DID.fromString("did:btcr:x705-jznz-q3nl-srs"), resolveOptions); 21 | System.out.println(resolveResult.toJson()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/src/main/java/uniresolver/examples/TestDriverDidSov.java: -------------------------------------------------------------------------------- 1 | package uniresolver.examples; 2 | 3 | import foundation.identity.did.DID; 4 | import uniresolver.driver.did.sov.DidSovDriver; 5 | import uniresolver.result.ResolveDataModelResult; 6 | import uniresolver.result.ResolveResult; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class TestDriverDidSov { 12 | 13 | public static void main(String[] args) throws Exception { 14 | 15 | DidSovDriver driver = new DidSovDriver(); 16 | driver.setLibIndyPath("./sovrin/lib/libindy.so"); 17 | driver.setPoolConfigs("_;./sovrin/mainnet.txn"); 18 | driver.setPoolVersions("_;2"); 19 | 20 | Map resolveOptions = new HashMap<>(); 21 | resolveOptions.put("accept", "application/did+ld+json"); 22 | 23 | ResolveResult resolveResult; 24 | resolveResult = driver.resolve(DID.fromString("did:sov:WRfXPg8dantKVubE3HX8pw"), resolveOptions); 25 | System.out.println(resolveResult.toJson()); 26 | resolveResult = driver.resolveRepresentation(DID.fromString("did:sov:WRfXPg8dantKVubE3HX8pw"), resolveOptions); 27 | System.out.println(resolveResult.toJson()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/src/main/java/uniresolver/examples/TestLocalUniDereferencer.java: -------------------------------------------------------------------------------- 1 | package uniresolver.examples; 2 | 3 | import uniresolver.driver.did.sov.DidSovDriver; 4 | import uniresolver.local.LocalUniDereferencer; 5 | import uniresolver.local.LocalUniResolver; 6 | import uniresolver.result.DereferenceResult; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class TestLocalUniDereferencer { 12 | 13 | public static void main(String[] args) throws Exception { 14 | 15 | LocalUniResolver uniResolver = new LocalUniResolver(); 16 | uniResolver.getDrivers().add(new DidSovDriver()); 17 | uniResolver.getDriver(DidSovDriver.class).setLibIndyPath("./sovrin/lib/libindy.so"); 18 | uniResolver.getDriver(DidSovDriver.class).setPoolConfigs("_;./sovrin/mainnet.txn"); 19 | uniResolver.getDriver(DidSovDriver.class).setPoolVersions("_;2"); 20 | 21 | LocalUniDereferencer uniDereferencer = new LocalUniDereferencer(); 22 | uniDereferencer.setUniResolver(uniResolver); 23 | 24 | Map dereferenceOptions = new HashMap<>(); 25 | dereferenceOptions.put("accept", "application/did+ld+json"); 26 | DereferenceResult dereferenceResult = uniDereferencer.dereference("did:sov:WRfXPg8dantKVubE3HX8pw#key-1", dereferenceOptions); 27 | System.out.println(dereferenceResult.toJson()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/src/main/java/uniresolver/examples/TestLocalUniResolver.java: -------------------------------------------------------------------------------- 1 | package uniresolver.examples; 2 | 3 | import uniresolver.driver.did.sov.DidSovDriver; 4 | import uniresolver.local.LocalUniResolver; 5 | import uniresolver.result.ResolveResult; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class TestLocalUniResolver { 11 | 12 | public static void main(String[] args) throws Exception { 13 | 14 | LocalUniResolver uniResolver = new LocalUniResolver(); 15 | uniResolver.getDrivers().add(new DidSovDriver()); 16 | uniResolver.getDriver(DidSovDriver.class).setLibIndyPath("./sovrin/lib/libindy.so"); 17 | uniResolver.getDriver(DidSovDriver.class).setPoolConfigs("_;./sovrin/mainnet.txn"); 18 | uniResolver.getDriver(DidSovDriver.class).setPoolVersions("_;2"); 19 | 20 | Map resolveOptions = new HashMap<>(); 21 | resolveOptions.put("accept", "application/did+ld+json"); 22 | 23 | ResolveResult resolveResult; 24 | resolveResult = uniResolver.resolve("did:sov:WRfXPg8dantKVubE3HX8pw", resolveOptions); 25 | System.out.println(resolveResult.toJson()); 26 | resolveResult = uniResolver.resolveRepresentation("did:sov:WRfXPg8dantKVubE3HX8pw", resolveOptions); 27 | System.out.println(resolveResult.toJson()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/src/main/java/uniresolver/examples/w3ctestsuite/TestDIDResolution.java: -------------------------------------------------------------------------------- 1 | package uniresolver.examples.w3ctestsuite; 2 | 3 | import uniresolver.ResolutionException; 4 | import uniresolver.client.ClientUniResolver; 5 | import uniresolver.result.ResolveResult; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class TestDIDResolution { 12 | 13 | static final Map> expectedOutcomes = Map.of( 14 | "defaultOutcome", List.of(0, 1), 15 | "notFoundErrorOutcome", List.of(2), 16 | "invalidDidErrorOutcome", List.of(3) 17 | ); 18 | 19 | static final List function = List.of( 20 | "resolve", 21 | "resolveRepresentation", 22 | "resolve", 23 | "resolve" 24 | ); 25 | 26 | static final List didString = List.of( 27 | "did:sov:WRfXPg8dantKVubE3HX8pw", 28 | "did:sov:WRfXPg8dantKVubE3HX8pw", 29 | "did:sov:0000000000000000000000", 30 | "did:sov:danube:_$::" 31 | ); 32 | 33 | static final List> resolutionOptions = List.of( 34 | Map.of(), 35 | Map.of( 36 | "accept", "application/did+ld+json" 37 | ), 38 | Map.of(), 39 | Map.of() 40 | ); 41 | 42 | public static void main(String[] args) throws Exception { 43 | 44 | ClientUniResolver uniResolver = new ClientUniResolver(); 45 | uniResolver.setResolveUri("http://localhost:8080/1.0/identifiers/"); 46 | 47 | List resolveResults = new ArrayList<>(); 48 | 49 | for (int i=0; i> expectedOutcomes = Map.of( 16 | "defaultOutcome", List.of(0, 1), 17 | "invalidDidUrlErrorOutcome", List.of(2), 18 | "notFoundErrorOutcome", List.of(3, 4) 19 | ); 20 | 21 | static final List function = List.of( 22 | "dereference", 23 | "dereference", 24 | "dereference", 25 | "dereference", 26 | "dereference" 27 | ); 28 | 29 | static final List didUrlString = List.of( 30 | "did:sov:WRfXPg8dantKVubE3HX8pw", 31 | "did:sov:WRfXPg8dantKVubE3HX8pw#key-1", 32 | "did:sov:WRfXPg8dantKVubE3HX8pw#key-1#key-2", 33 | "did:sov:WRfXPg8dantKVubE3HX8pw#key-3", 34 | "did:sov:0000000000000000000000" 35 | ); 36 | 37 | public static void main(String[] args) throws Exception { 38 | 39 | Map dereferenceOptions = new HashMap<>(); 40 | dereferenceOptions.put("accept", "application/did+ld+json"); 41 | 42 | ClientUniResolver uniResolver = new ClientUniResolver(); 43 | uniResolver.setResolveUri("http://localhost:8080/1.0/identifiers/"); 44 | 45 | LocalUniDereferencer uniDereferencer = new LocalUniDereferencer(); 46 | uniDereferencer.setUniResolver(uniResolver); 47 | 48 | List dereferenceResults = new ArrayList<>(); 49 | 50 | for (int i=0; i dids = new ArrayList<>(); 20 | 21 | static final Map didParameters = new LinkedHashMap<>(); 22 | 23 | public static void main(String[] args) throws Exception { 24 | 25 | DID did = DID.fromString("did:sov:WRfXPg8dantKVubE3HX8pw"); 26 | dids.add(did.toString()); 27 | 28 | DIDURL didUrl1 = DIDURL.fromString(did + "?" + Parameters.DID_URL_PARAMETER_SERVICE + "=" + "files" + "&" + Parameters.DID_URL_PARAMETER_RELATIVEREF + "=" + URLEncoder.encode("/myresume/doc?version=latest#intro", StandardCharsets.UTF_8)); 29 | DIDURL didUrl2 = DIDURL.fromString(did + "?" + Parameters.DID_URL_PARAMETER_HL + "=" + "zQmWvQxTqbG2Z9HPJgG57jjwR154cKhbtJenbyYTWkjgF3e"); 30 | DIDURL didUrl3 = DIDURL.fromString(did + "?" + Parameters.DID_URL_PARAMETER_VERSIONID + "=" + "4"); 31 | DIDURL didUrl4 = DIDURL.fromString(did + "?" + Parameters.DID_URL_PARAMETER_VERSIONTIME + "=" + "2016-10-17T02:41:00Z"); 32 | 33 | didParameters.put(Parameters.DID_URL_PARAMETER_SERVICE.toString(), didUrl1.toString()); 34 | didParameters.put(Parameters.DID_URL_PARAMETER_RELATIVEREF.toString(), didUrl1.toString()); 35 | didParameters.put(Parameters.DID_URL_PARAMETER_HL.toString(), didUrl2.toString()); 36 | didParameters.put(Parameters.DID_URL_PARAMETER_VERSIONID.toString(), didUrl3.toString()); 37 | didParameters.put(Parameters.DID_URL_PARAMETER_VERSIONTIME.toString(), didUrl4.toString()); 38 | 39 | System.out.println(TestSuiteUtil.makeIdentifierTestSuiteReport("sov", dids, didParameters)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/src/main/java/uniresolver/examples/w3ctestsuite/TestSuiteUtil.java: -------------------------------------------------------------------------------- 1 | package uniresolver.examples.w3ctestsuite; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import foundation.identity.did.DID; 5 | import uniresolver.result.DereferenceResult; 6 | import uniresolver.result.ResolveResult; 7 | 8 | import java.util.*; 9 | 10 | public class TestSuiteUtil { 11 | 12 | private static final ObjectMapper objectMapper = new ObjectMapper(); 13 | 14 | static String makeIdentifierTestSuiteReport(String didMethod, List dids, Map didParameters) throws Exception { 15 | 16 | Map json = new LinkedHashMap<>(); 17 | json.put("implementation", "Universal Resolver"); 18 | json.put("implementer", "Decentralized Identity Foundation and Contributors"); 19 | json.put("didMethod", didMethod); 20 | json.put("dids", dids); 21 | json.put("didParameters", didParameters); 22 | 23 | return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(json); 24 | } 25 | 26 | static String makeDidResolutionTestSuiteReport(Map> expectedOutcomes, List function, List didString, String didMethod, List> resolutionOptions, List resolveResults) throws Exception { 27 | 28 | List executions = new ArrayList<>(); 29 | 30 | for (int i=0; i input = new LinkedHashMap<>(); 33 | input.put("did", didString.get(i)); 34 | input.put("resolutionOptions", resolutionOptions.get(i)); 35 | 36 | Map output = resolveResults.get(i).toMap(); 37 | 38 | Map execution = new LinkedHashMap<>(); 39 | execution.put("function", function.get(i)); 40 | execution.put("input", input); 41 | execution.put("output", output); 42 | 43 | executions.add(execution); 44 | } 45 | 46 | Map json = new LinkedHashMap<>(); 47 | json.put("implementation", "Universal Resolver"); 48 | json.put("implementer", "Decentralized Identity Foundation and Contributors"); 49 | json.put("didMethod", didMethod); 50 | json.put("expectedOutcomes", expectedOutcomes); 51 | json.put("executions", executions); 52 | 53 | return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(json); 54 | } 55 | 56 | static String makeDidUrlDereferencingTestSuiteReport(Map> expectedOutcomes, List function, List didUrlString, String didMethod, Map dereferenceOptions, List dereferenceResults) throws Exception { 57 | 58 | List executions = new ArrayList<>(); 59 | 60 | for (int i=0; i input = new LinkedHashMap<>(); 63 | input.put("didUrl", didUrlString.get(i)); 64 | input.put("dereferenceOptions", dereferenceOptions); 65 | 66 | Map output = dereferenceResults.get(i).toMap(); 67 | 68 | Map execution = new LinkedHashMap<>(); 69 | execution.put("function", function.get(i)); 70 | execution.put("input", input); 71 | execution.put("output", output); 72 | 73 | executions.add(execution); 74 | } 75 | 76 | Map json = new LinkedHashMap<>(); 77 | json.put("implementation", "Universal Resolver"); 78 | json.put("implementer", "Decentralized Identity Foundation and Contributors"); 79 | json.put("didMethod", didMethod); 80 | json.put("expectedOutcomes", expectedOutcomes); 81 | json.put("executions", executions); 82 | 83 | return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(json); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /examples/src/main/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | status=error 2 | dest=err 3 | name=testLoggerConfig 4 | 5 | filter.threshold.type=ThresholdFilter 6 | filter.threshold.level=debug 7 | 8 | appender.console.type=Console 9 | appender.console.name=STDOUT 10 | appender.console.layout.type=PatternLayout 11 | appender.console.layout.pattern=%highlight{[%t] %p %c{2} (%M) -} %highlight{%m%n%throwable}{STYLE=Logback} 12 | appender.console.filter.threshold.type=ThresholdFilter 13 | appender.console.filter.threshold.level=debug 14 | 15 | logger.uniresolver.name=uniresolver 16 | logger.uniresolver.level=debug 17 | 18 | logger.timeoutHandler.name=org.bitcoinj.core.PeerSocketHandler 19 | logger.timeoutHandler.level=error 20 | 21 | rootLogger.level=info 22 | rootLogger.additivity=false 23 | rootLogger.appenderRefs=stdout 24 | rootLogger.appenderRef.stdout.ref=STDOUT 25 | -------------------------------------------------------------------------------- /uni-resolver-client/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /uni-resolver-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | uni-resolver-client 5 | jar 6 | uni-resolver-client 7 | 8 | 9 | decentralized-identity 10 | uni-resolver 11 | 0.33-SNAPSHOT 12 | 13 | 14 | 15 | 1.18.0 16 | 17 | 18 | 19 | 20 | 21 | commons-codec 22 | commons-codec 23 | ${commons-codec.version} 24 | 25 | 26 | 27 | 28 | 29 | 30 | decentralized-identity 31 | uni-resolver-core 32 | 33 | 34 | org.apache.httpcomponents 35 | httpclient 36 | 37 | 38 | commons-codec 39 | commons-codec 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /uni-resolver-client/src/main/java/uniresolver/client/ClientUniDereferencer.java: -------------------------------------------------------------------------------- 1 | package uniresolver.client; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.apache.http.HttpEntity; 5 | import org.apache.http.client.HttpClient; 6 | import org.apache.http.client.methods.CloseableHttpResponse; 7 | import org.apache.http.client.methods.HttpGet; 8 | import org.apache.http.entity.ContentType; 9 | import org.apache.http.impl.client.HttpClients; 10 | import org.apache.http.protocol.HTTP; 11 | import org.apache.http.util.EntityUtils; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import uniresolver.DereferencingException; 15 | import uniresolver.ResolutionException; 16 | import uniresolver.UniDereferencer; 17 | import uniresolver.result.DereferenceResult; 18 | import uniresolver.util.HttpBindingClientUtil; 19 | 20 | import java.net.URI; 21 | import java.nio.charset.Charset; 22 | import java.util.Arrays; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | 27 | public class ClientUniDereferencer implements UniDereferencer { 28 | 29 | private static final Logger log = LoggerFactory.getLogger(ClientUniDereferencer.class); 30 | 31 | private static final ObjectMapper objectMapper = new ObjectMapper(); 32 | 33 | public static final HttpClient DEFAULT_HTTP_CLIENT = HttpClients.createDefault(); 34 | public static final URI DEFAULT_DEREFERENCE_URI = URI.create("http://localhost:8080/1.0/identifiers"); 35 | 36 | private HttpClient httpClient = DEFAULT_HTTP_CLIENT; 37 | private URI dereferenceUri = DEFAULT_DEREFERENCE_URI; 38 | 39 | public ClientUniDereferencer() { 40 | 41 | } 42 | 43 | public static ClientUniDereferencer create(URI baseUri) { 44 | 45 | if (! baseUri.toString().endsWith("/")) baseUri = URI.create(baseUri + "/"); 46 | 47 | ClientUniDereferencer clientUniDereferencer = new ClientUniDereferencer(); 48 | clientUniDereferencer.setDereferenceUri(URI.create(baseUri + "identifiers")); 49 | 50 | return clientUniDereferencer; 51 | } 52 | 53 | @Override 54 | public DereferenceResult dereference(String didUrlString, Map dereferenceOptions) throws DereferencingException, ResolutionException { 55 | 56 | if (log.isDebugEnabled()) log.debug("dereference(" + didUrlString + ") with options: " + dereferenceOptions); 57 | 58 | if (didUrlString == null) throw new NullPointerException(); 59 | if (dereferenceOptions == null) dereferenceOptions = new HashMap<>(); 60 | 61 | // set HTTP URI 62 | 63 | String uriString = this.getDereferenceUri().toString(); 64 | 65 | if (! uriString.endsWith("/")) uriString += "/"; 66 | uriString += didUrlString; 67 | 68 | // set Accept header 69 | 70 | String accept = (String) dereferenceOptions.get("accept"); 71 | if (accept == null) throw new DereferencingException("No 'accept' provided in 'dereferenceOptions' for dereference()."); 72 | 73 | List acceptMediaTypes = Arrays.asList(DereferenceResult.MEDIA_TYPE, accept); 74 | String acceptMediaTypesString = String.join(",", acceptMediaTypes); 75 | 76 | if (log.isDebugEnabled()) log.debug("Setting Accept: header to " + acceptMediaTypesString); 77 | 78 | // prepare HTTP request 79 | 80 | HttpGet httpGet = new HttpGet(URI.create(uriString)); 81 | httpGet.addHeader("Accept", acceptMediaTypesString); 82 | 83 | // execute HTTP request and read response 84 | 85 | DereferenceResult dereferenceResult = null; 86 | 87 | if (log.isDebugEnabled()) log.debug("Request for DID " + didUrlString + " to " + uriString + " with Accept: header " + acceptMediaTypesString); 88 | 89 | try (CloseableHttpResponse httpResponse = (CloseableHttpResponse) this.getHttpClient().execute(httpGet)) { 90 | 91 | // execute HTTP request 92 | 93 | HttpEntity httpEntity = httpResponse.getEntity(); 94 | int httpStatusCode = httpResponse.getStatusLine().getStatusCode(); 95 | String httpStatusMessage = httpResponse.getStatusLine().getReasonPhrase(); 96 | ContentType httpContentType = ContentType.get(httpResponse.getEntity()); 97 | Charset httpCharset = (httpContentType != null && httpContentType.getCharset() != null) ? httpContentType.getCharset() : HTTP.DEF_CONTENT_CHARSET; 98 | 99 | if (log.isDebugEnabled()) log.debug("Response HTTP status from " + uriString + ": " + httpStatusCode + " " + httpStatusMessage); 100 | if (log.isDebugEnabled()) log.debug("Response HTTP content type from " + uriString + ": " + httpContentType + " / " + httpCharset); 101 | 102 | // read result 103 | 104 | byte[] httpBodyBytes = EntityUtils.toByteArray(httpEntity); 105 | String httpBodyString = new String(httpBodyBytes, httpCharset); 106 | EntityUtils.consume(httpEntity); 107 | 108 | if (log.isDebugEnabled()) log.debug("Response HTTP body from " + uriString + ": " + httpBodyString); 109 | 110 | if (httpContentType != null && (DereferenceResult.isMediaType(httpContentType) || HttpBindingClientUtil.isDereferenceResultHttpContent(httpBodyString))) { 111 | dereferenceResult = HttpBindingClientUtil.fromHttpBodyDereferenceResult(httpBodyString); 112 | } 113 | 114 | if (httpStatusCode == 404 && dereferenceResult == null) { 115 | throw new DereferencingException(DereferencingException.ERROR_NOTFOUND, httpStatusCode + " " + httpStatusMessage + " (" + httpBodyString + ")"); 116 | } 117 | 118 | if (httpStatusCode == 406 && dereferenceResult == null) { 119 | throw new DereferencingException(DereferencingException.ERROR_CONTENTTYPENOTSUPPORTED, httpStatusCode + " " + httpStatusMessage + " (" + httpBodyString + ")"); 120 | } 121 | 122 | if (httpStatusCode != 200 && dereferenceResult == null) { 123 | throw new DereferencingException(DereferencingException.ERROR_INTERNALERROR, "Cannot retrieve DEREFERENCE result for " + didUrlString + ": " + httpStatusCode + " " + httpStatusMessage + " (" + httpBodyString + ")"); 124 | } 125 | 126 | if (dereferenceResult != null && dereferenceResult.isErrorResult()) { 127 | if (log.isWarnEnabled()) log.warn("Received DEREFERENCE result: " + dereferenceResult.getError() + " -> " + dereferenceResult.getErrorMessage()); 128 | throw DereferencingException.fromDereferenceResult(dereferenceResult); 129 | } 130 | 131 | if (dereferenceResult == null) { 132 | dereferenceResult = HttpBindingClientUtil.fromHttpBodyContent(httpBodyBytes, httpContentType); 133 | } 134 | } catch (DereferencingException ex) { 135 | 136 | throw ex; 137 | } catch (Exception ex) { 138 | 139 | throw new DereferencingException("Cannot retrieve DEREFERENCE result for " + didUrlString + " from " + uriString + ": " + ex.getMessage(), ex); 140 | } 141 | 142 | if (log.isDebugEnabled()) log.debug("Retrieved DEREFERENCE result for " + didUrlString + " (" + uriString + "): " + dereferenceResult); 143 | 144 | // done 145 | 146 | return dereferenceResult; 147 | } 148 | 149 | /* 150 | * Getters and setters 151 | */ 152 | 153 | public HttpClient getHttpClient() { 154 | return this.httpClient; 155 | } 156 | 157 | public void setHttpClient(HttpClient httpClient) { 158 | this.httpClient = httpClient; 159 | } 160 | 161 | public URI getDereferenceUri() { 162 | return this.dereferenceUri; 163 | } 164 | 165 | public void setDereferenceUri(URI dereferenceUri) { 166 | this.dereferenceUri = dereferenceUri; 167 | } 168 | 169 | public void setDereferenceUri(String dereferenceUri) { 170 | this.dereferenceUri = URI.create(dereferenceUri); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /uni-resolver-core/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /uni-resolver-core/openapi/java-client-generated/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !.openapi-generator-ignore -------------------------------------------------------------------------------- /uni-resolver-core/openapi/java-client-generated/.openapi-generator-ignore: -------------------------------------------------------------------------------- 1 | .gitignore -------------------------------------------------------------------------------- /uni-resolver-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | uni-resolver-core 5 | jar 6 | uni-resolver-core 7 | 8 | 9 | decentralized-identity 10 | uni-resolver 11 | 0.33-SNAPSHOT 12 | 13 | 14 | 15 | 16 | 17 | org.apache.maven.plugins 18 | maven-clean-plugin 19 | 20 | 21 | 22 | ${project.basedir}/openapi/java-client-generated/ 23 | 24 | **/* 25 | 26 | 27 | .gitignore 28 | .openapi-generator-ignore 29 | 30 | false 31 | 32 | 33 | 34 | 35 | 36 | org.openapitools 37 | openapi-generator-maven-plugin 38 | 39 | 40 | execution-universal-resolver 41 | 42 | generate 43 | 44 | 45 | java 46 | ${project.parent.basedir}/openapi/openapi.yaml 47 | openapi/java-client-generated/ 48 | uniresolver.openapi.api 49 | uniresolver.openapi.model 50 | uniresolver.openapi 51 | REF_AS_PARENT_IN_ALLOF=true 52 | 53 | uniresolver 54 | src/main/java/ 55 | decentralized-identity 56 | uniresolver-openapi 57 | ${project.version} 58 | uniresolver.openapi.config 59 | false 60 | false 61 | native 62 | true 63 | java8 64 | true 65 | false 66 | true 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | decentralized-identity 78 | did-common-java 79 | 80 | 81 | com.fasterxml.jackson.core 82 | jackson-core 83 | 84 | 85 | com.fasterxml.jackson.core 86 | jackson-databind 87 | 88 | 89 | com.fasterxml.jackson.datatype 90 | jackson-datatype-jsr310 91 | 92 | 93 | jakarta.annotation 94 | jakarta.annotation-api 95 | 96 | 97 | jakarta.validation 98 | jakarta.validation-api 99 | 100 | 101 | org.apache.httpcomponents 102 | httpcore 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/DereferencingException.java: -------------------------------------------------------------------------------- 1 | package uniresolver; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import uniresolver.result.DereferenceResult; 6 | 7 | import java.util.Map; 8 | 9 | public class DereferencingException extends Exception { 10 | 11 | public static final String ERROR_INVALIDDIDURL = "invalidDidUrl"; 12 | public static final String ERROR_NOTFOUND = "notFound"; 13 | public static final String ERROR_CONTENTTYPENOTSUPPORTED = "contentTypeNotSupported"; 14 | public static final String ERROR_METHODNOTSUPPORTED = "methodNotSupported"; 15 | public static final String ERROR_INTERNALERROR = "internalError"; 16 | 17 | private static final Logger log = LoggerFactory.getLogger(DereferencingException.class); 18 | 19 | private final String error; 20 | private final Map dereferencingMetadata; 21 | 22 | private DereferenceResult dereferenceResult; 23 | 24 | public DereferencingException(String error, String message, Map dereferencingMetadata, Throwable ex) { 25 | super(message, ex); 26 | this.error = error; 27 | this.dereferencingMetadata = dereferencingMetadata; 28 | } 29 | 30 | public DereferencingException(String error, String message, Map dereferencingMetadata) { 31 | super(message); 32 | this.error = error; 33 | this.dereferencingMetadata = dereferencingMetadata; 34 | } 35 | 36 | public DereferencingException(String error, String message, Throwable ex) { 37 | this(error, message, (Map) null, ex); 38 | } 39 | 40 | public DereferencingException(String error, String message) { 41 | this(error, message, (Map) null); 42 | } 43 | 44 | public DereferencingException(String message, Throwable ex) { 45 | this(ERROR_INTERNALERROR, message, ex); 46 | } 47 | 48 | public DereferencingException(String message) { 49 | this(ERROR_INTERNALERROR, message); 50 | } 51 | 52 | public static DereferencingException fromDereferenceResult(DereferenceResult dereferenceResult) { 53 | if (dereferenceResult != null && dereferenceResult.isErrorResult()) { 54 | DereferencingException dereferencingException = new DereferencingException(dereferenceResult.getError(), dereferenceResult.getErrorMessage(), dereferenceResult.getDereferencingMetadata()); 55 | dereferencingException.dereferenceResult = dereferenceResult; 56 | return dereferencingException; 57 | } else { 58 | throw new IllegalArgumentException("No error result: " + dereferenceResult); 59 | } 60 | } 61 | 62 | /* 63 | * Error methods 64 | */ 65 | 66 | public DereferenceResult toErrorDereferenceResult() { 67 | if (this.dereferenceResult != null) return this.dereferenceResult; 68 | DereferenceResult dereferenceResult = DereferenceResult.build(); 69 | if (this.getError() != null) dereferenceResult.setError(this.getError()); 70 | if (this.getMessage() != null) dereferenceResult.setErrorMessage(this.getMessage()); 71 | if (this.getDereferencingMetadata() != null) dereferenceResult.getDereferencingMetadata().putAll(this.getDereferencingMetadata()); 72 | dereferenceResult.setContent(null); 73 | if (log.isDebugEnabled()) log.debug("Created error dereference result: " + dereferenceResult); 74 | return dereferenceResult; 75 | } 76 | 77 | /* 78 | * Getters and setters 79 | */ 80 | 81 | public String getError() { 82 | return this.error; 83 | } 84 | 85 | public Map getDereferencingMetadata() { 86 | return dereferencingMetadata; 87 | } 88 | } 89 | 90 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/ResolutionException.java: -------------------------------------------------------------------------------- 1 | package uniresolver; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import uniresolver.result.ResolveResult; 6 | 7 | import java.util.Map; 8 | 9 | public class ResolutionException extends Exception { 10 | 11 | public static final String ERROR_INVALIDDID = "invalidDid"; 12 | public static final String ERROR_NOTFOUND = "notFound"; 13 | public static final String ERROR_REPRESENTATIONNOTSUPPORTED = "representationNotSupported"; 14 | public static final String ERROR_METHODNOTSUPPORTED = "methodNotSupported"; 15 | public static final String ERROR_INTERNALERROR = "internalError"; 16 | 17 | private static final Logger log = LoggerFactory.getLogger(ResolutionException.class); 18 | 19 | private final String error; 20 | private final Map didResolutionMetadata; 21 | 22 | private ResolveResult resolveResult; 23 | 24 | public ResolutionException(String error, String message, Map didResolutionMetadata, Throwable ex) { 25 | super(message, ex); 26 | this.error = error; 27 | this.didResolutionMetadata = didResolutionMetadata; 28 | } 29 | 30 | public ResolutionException(String error, String message, Map didResolutionMetadata) { 31 | super(message); 32 | this.error = error; 33 | this.didResolutionMetadata = didResolutionMetadata; 34 | } 35 | 36 | public ResolutionException(String error, String message, Throwable ex) { 37 | this(error, message, (Map) null, ex); 38 | } 39 | 40 | public ResolutionException(String error, String message) { 41 | this(error, message, (Map) null); 42 | } 43 | 44 | public ResolutionException(String message, Throwable ex) { 45 | this(ERROR_INTERNALERROR, message, ex); 46 | } 47 | 48 | public ResolutionException(String message) { 49 | this(ERROR_INTERNALERROR, message); 50 | } 51 | 52 | public ResolutionException(Throwable ex) { 53 | this(ex.getMessage(), ex); 54 | } 55 | 56 | public static ResolutionException fromResolveResult(ResolveResult resolveResult) { 57 | if (resolveResult != null && resolveResult.isErrorResult()) { 58 | ResolutionException resolutionException = new ResolutionException(resolveResult.getError(), resolveResult.getErrorMessage(), resolveResult.getDidResolutionMetadata()); 59 | resolutionException.resolveResult = resolveResult; 60 | return resolutionException; 61 | } else { 62 | throw new IllegalArgumentException("No error result: " + resolveResult); 63 | } 64 | } 65 | 66 | /* 67 | * Error methods 68 | */ 69 | 70 | public ResolveResult toErrorResolveResult() { 71 | if (this.resolveResult != null) return this.resolveResult; 72 | ResolveResult resolveResult = ResolveResult.build(); 73 | if (this.getError() != null) resolveResult.setError(this.getError()); 74 | if (this.getMessage() != null) resolveResult.setErrorMessage(this.getMessage()); 75 | if (this.getDidResolutionMetadata() != null) resolveResult.getDidResolutionMetadata().putAll(this.getDidResolutionMetadata()); 76 | resolveResult.setDidDocument(null); 77 | if (log.isDebugEnabled()) log.debug("Created error resolve result: " + resolveResult); 78 | return resolveResult; 79 | } 80 | 81 | /* 82 | * Getters and setters 83 | */ 84 | 85 | public String getError() { 86 | return error; 87 | } 88 | 89 | public Map getDidResolutionMetadata() { 90 | return didResolutionMetadata; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/UniDereferencer.java: -------------------------------------------------------------------------------- 1 | package uniresolver; 2 | 3 | import uniresolver.result.DereferenceResult; 4 | import uniresolver.w3c.DIDURLDereferencer; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public interface UniDereferencer extends DIDURLDereferencer { 10 | 11 | @Override public DereferenceResult dereference(String didUrlString, Map dereferenceOptions) throws DereferencingException, ResolutionException; 12 | 13 | default public DereferenceResult dereference(String didUrlString) throws DereferencingException, ResolutionException { 14 | return this.dereference(didUrlString, new HashMap<>()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/UniResolver.java: -------------------------------------------------------------------------------- 1 | package uniresolver; 2 | 3 | import foundation.identity.did.representations.Representations; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import uniresolver.result.ResolveResult; 7 | import uniresolver.w3c.DIDResolver; 8 | 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | public interface UniResolver extends DIDResolver { 15 | 16 | public static final String PROPERTIES_MEDIA_TYPE = "application/json"; 17 | public static final String METHODS_MEDIA_TYPE = "application/json"; 18 | public static final String TEST_IDENTIFIER_MEDIA_TYPE = "application/json"; 19 | public static final String TRAITS_MEDIA_TYPE = "application/json"; 20 | 21 | static final Logger log = LoggerFactory.getLogger(UniResolver.class); 22 | 23 | @Override public ResolveResult resolve(String didString, Map resolutionOptions) throws ResolutionException; 24 | 25 | default public ResolveResult resolve(String didString) throws ResolutionException { 26 | return this.resolve(didString, Map.of("accept", Representations.DEFAULT_MEDIA_TYPE)); 27 | } 28 | 29 | public Map> properties() throws ResolutionException; 30 | public Set methods() throws ResolutionException; 31 | public Map> testIdentifiers() throws ResolutionException; 32 | public Map> traits() throws ResolutionException; 33 | } 34 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/result/DereferenceResult.java: -------------------------------------------------------------------------------- 1 | package uniresolver.result; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import org.apache.commons.codec.DecoderException; 8 | import org.apache.commons.codec.binary.Hex; 9 | import org.apache.http.entity.ContentType; 10 | 11 | import java.io.IOException; 12 | import java.io.Reader; 13 | import java.net.URI; 14 | import java.nio.charset.StandardCharsets; 15 | import java.util.LinkedHashMap; 16 | import java.util.Map; 17 | 18 | @JsonPropertyOrder({ "dereferencingMetadata", "content", "contentMetadata" }) 19 | @JsonIgnoreProperties(ignoreUnknown=true) 20 | public class DereferenceResult implements Result { 21 | 22 | public static final String MEDIA_TYPE = "application/ld+json;profile=\"https://w3id.org/did-url-dereferencing\""; 23 | public static final ContentType CONTENT_TYPE = ContentType.parse(MEDIA_TYPE); 24 | 25 | private static final URI DEFAULT_JSONLD_CONTEXT = URI.create("https://w3id.org/did-resolution/v1"); 26 | 27 | private static final ObjectMapper objectMapper = new ObjectMapper(); 28 | 29 | @JsonProperty("dereferencingMetadata") 30 | private Map dereferencingMetadata; 31 | 32 | @JsonProperty("content") 33 | private byte[] content; 34 | 35 | @JsonProperty("contentMetadata") 36 | private Map contentMetadata; 37 | 38 | private DereferenceResult(Map dereferencingMetadata, byte[] content, Map contentMetadata) { 39 | this.dereferencingMetadata = dereferencingMetadata != null ? dereferencingMetadata : new LinkedHashMap<>(); 40 | this.content = content; 41 | this.contentMetadata = contentMetadata != null ? contentMetadata : new LinkedHashMap<>(); 42 | } 43 | 44 | /* 45 | * Factory methods 46 | */ 47 | 48 | @JsonCreator 49 | public static DereferenceResult build(@JsonProperty(value="dereferencingMetadata") Map dereferencingMetadata, @JsonProperty(value="content", required=true) byte[] content, @JsonProperty(value="contentMetadata") Map contentMetadata) { 50 | return new DereferenceResult(dereferencingMetadata, content, contentMetadata); 51 | } 52 | 53 | public static DereferenceResult build() { 54 | return new DereferenceResult(new LinkedHashMap<>(), null, new LinkedHashMap<>()); 55 | } 56 | 57 | /* 58 | * Field methods 59 | */ 60 | 61 | @Override 62 | public Map getFunctionMetadata() { 63 | return this.getDereferencingMetadata(); 64 | } 65 | 66 | @Override 67 | public byte[] getFunctionContent() { 68 | return this.getContent(); 69 | } 70 | 71 | @Override 72 | public Map getFunctionContentMetadata() { 73 | return this.getContentMetadata(); 74 | } 75 | 76 | /* 77 | * Serialization 78 | */ 79 | 80 | public static DereferenceResult fromJson(String json) throws IOException { 81 | return objectMapper.readValue(json, DereferenceResult.class); 82 | } 83 | 84 | public static DereferenceResult fromJson(Reader reader) throws IOException { 85 | return objectMapper.readValue(reader, DereferenceResult.class); 86 | } 87 | 88 | @Override 89 | public Map toMap() { 90 | return objectMapper.convertValue(this, LinkedHashMap.class); 91 | } 92 | 93 | @Override 94 | public String toJson() { 95 | try { 96 | return objectMapper.writeValueAsString(this); 97 | } catch (JsonProcessingException ex) { 98 | throw new RuntimeException("Cannot write JSON: " + ex.getMessage(), ex); 99 | } 100 | } 101 | 102 | @Override 103 | public URI getDefaultContext() { 104 | return DEFAULT_JSONLD_CONTEXT; 105 | } 106 | 107 | @Override 108 | public boolean isComplete() { 109 | return this.getContentType() != null && this.getContent() != null; 110 | } 111 | 112 | /* 113 | * Getters and setters 114 | */ 115 | 116 | @JsonGetter("dereferencingMetadata") 117 | public final Map getDereferencingMetadata() { 118 | return this.dereferencingMetadata; 119 | } 120 | 121 | @JsonSetter("dereferencingMetadata") 122 | public final void setDereferencingMetadata(Map dereferencingMetadata) { 123 | this.dereferencingMetadata = dereferencingMetadata; 124 | } 125 | 126 | public final byte[] getContent() { 127 | return this.content; 128 | } 129 | 130 | @JsonGetter("content") 131 | public final String getContentAsString() { 132 | if (this.getContent() == null) { 133 | return null; 134 | } else { 135 | try { 136 | try (JsonParser jsonParser = objectMapper.getFactory().createParser(this.getContent())) { 137 | if (jsonParser.readValueAsTree() != null) return new String(this.getContent(), StandardCharsets.UTF_8); 138 | } 139 | } catch (IOException ignored) { 140 | } 141 | return Hex.encodeHexString(this.getContent()); 142 | } 143 | } 144 | 145 | public final void setContent(byte[] content) { 146 | this.content = content; 147 | } 148 | 149 | @JsonSetter("content") 150 | public final void setContentAsString(String contentString) { 151 | if (contentString == null) { 152 | this.setContent(null); 153 | } else { 154 | try { 155 | this.setContent(Hex.decodeHex(contentString)); 156 | } catch (DecoderException ex) { 157 | this.setContent(contentString.getBytes(StandardCharsets.UTF_8)); 158 | } 159 | } 160 | } 161 | 162 | @JsonGetter("contentMetadata") 163 | public final Map getContentMetadata() { 164 | return this.contentMetadata; 165 | } 166 | 167 | @JsonSetter("contentMetadata") 168 | public final void setContentMetadata(Map contentMetadata) { 169 | this.contentMetadata = contentMetadata; 170 | } 171 | 172 | /* 173 | * Helper methods 174 | */ 175 | 176 | @JsonIgnore 177 | public static boolean isMediaType(ContentType mediaType) { 178 | boolean isResolveResultMimeTypeEquals = CONTENT_TYPE.getMimeType().equals(mediaType.getMimeType()); 179 | boolean isResolveResultProfileEquals = CONTENT_TYPE.getParameter("profile").equals(mediaType.getParameter("profile")); 180 | return isResolveResultMimeTypeEquals && isResolveResultProfileEquals; 181 | } 182 | 183 | /* 184 | * Object methods 185 | */ 186 | 187 | @Override 188 | public String toString() { 189 | return this.toJson(); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/result/ResolveResult.java: -------------------------------------------------------------------------------- 1 | package uniresolver.result; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import foundation.identity.did.DIDDocument; 7 | import foundation.identity.did.representations.production.RepresentationProducer; 8 | import org.apache.http.entity.ContentType; 9 | 10 | import java.io.IOException; 11 | import java.io.Reader; 12 | import java.net.URI; 13 | import java.util.LinkedHashMap; 14 | import java.util.Map; 15 | 16 | @JsonPropertyOrder({ "didResolutionMetadata", "didDocument", "didDocumentMetadata" }) 17 | @JsonIgnoreProperties(ignoreUnknown=true) 18 | public class ResolveResult implements Result { 19 | 20 | public static final String MEDIA_TYPE = "application/ld+json;profile=\"https://w3id.org/did-resolution\""; 21 | public static final ContentType CONTENT_TYPE = ContentType.parse(MEDIA_TYPE); 22 | 23 | private static final URI DEFAULT_JSONLD_CONTEXT = URI.create("https://w3id.org/did-resolution/v1"); 24 | 25 | private static final ObjectMapper objectMapper = new ObjectMapper(); 26 | 27 | @JsonProperty("didResolutionMetadata") 28 | private Map didResolutionMetadata; 29 | 30 | @JsonProperty("didDocument") 31 | private DIDDocument didDocument; 32 | 33 | @JsonProperty("didDocumentMetadata") 34 | private Map didDocumentMetadata; 35 | 36 | private ResolveResult(Map didResolutionMetadata, DIDDocument didDocument, Map didDocumentMetadata) { 37 | this.didResolutionMetadata = didResolutionMetadata != null ? didResolutionMetadata : new LinkedHashMap<>(); 38 | this.didDocument = didDocument; 39 | this.didDocumentMetadata = didDocumentMetadata != null ? didDocumentMetadata : new LinkedHashMap<>(); 40 | } 41 | 42 | /* 43 | * Factory methods 44 | */ 45 | 46 | @JsonCreator 47 | public static ResolveResult build(@JsonProperty(value="didResolutionMetadata") Map didResolutionMetadata, @JsonProperty(value="didDocument") DIDDocument didDocument, @JsonProperty(value="didDocumentMetadata") Map didDocumentMetadata) { 48 | return new ResolveResult(didResolutionMetadata, didDocument, didDocumentMetadata); 49 | } 50 | 51 | public static ResolveResult build() { 52 | return new ResolveResult(new LinkedHashMap<>(), null, new LinkedHashMap<>()); 53 | } 54 | 55 | /* 56 | * Field methods 57 | */ 58 | 59 | @Override 60 | public Map getFunctionMetadata() { 61 | return this.getDidResolutionMetadata(); 62 | } 63 | 64 | @Override 65 | public byte[] getFunctionContent() throws IOException { 66 | if (this.getDidDocument() == null) return null; 67 | return RepresentationProducer.produce(this.getDidDocument(), this.getContentType()); 68 | } 69 | 70 | @Override 71 | public Map getFunctionContentMetadata() { 72 | return this.getDidDocumentMetadata(); 73 | } 74 | 75 | /* 76 | * Serialization 77 | */ 78 | 79 | public static ResolveResult fromJson(String json) throws IOException { 80 | return objectMapper.readValue(json, ResolveResult.class); 81 | } 82 | 83 | public static ResolveResult fromJson(Reader reader) throws IOException { 84 | return objectMapper.readValue(reader, ResolveResult.class); 85 | } 86 | 87 | @Override 88 | public Map toMap() { 89 | return objectMapper.convertValue(this, LinkedHashMap.class); 90 | } 91 | 92 | @Override 93 | public String toJson() { 94 | try { 95 | return objectMapper.writeValueAsString(this); 96 | } catch (JsonProcessingException ex) { 97 | throw new RuntimeException("Cannot write JSON: " + ex.getMessage(), ex); 98 | } 99 | } 100 | 101 | @Override 102 | public URI getDefaultContext() { 103 | return DEFAULT_JSONLD_CONTEXT; 104 | } 105 | 106 | @Override 107 | public boolean isComplete() { 108 | return this.getContentType() != null && this.getDidDocument() != null; 109 | } 110 | 111 | /* 112 | * Getters and setters 113 | */ 114 | 115 | @JsonGetter("didResolutionMetadata") 116 | public final Map getDidResolutionMetadata() { 117 | return this.didResolutionMetadata; 118 | } 119 | 120 | @JsonSetter("didResolutionMetadata") 121 | public final void setDidResolutionMetadata(Map didResolutionMetadata) { 122 | this.didResolutionMetadata = didResolutionMetadata; 123 | } 124 | 125 | @JsonGetter("didDocument") 126 | public DIDDocument getDidDocument() { 127 | return this.didDocument; 128 | } 129 | 130 | @JsonSetter("didDocument") 131 | public void setDidDocument(DIDDocument didDocument) { 132 | this.didDocument = didDocument; 133 | } 134 | 135 | @JsonGetter("didDocumentMetadata") 136 | public final Map getDidDocumentMetadata() { 137 | return this.didDocumentMetadata; 138 | } 139 | 140 | @JsonSetter("didDocumentMetadata") 141 | public final void setDidDocumentMetadata(Map didDocumentMetadata) { 142 | this.didDocumentMetadata = didDocumentMetadata; 143 | } 144 | 145 | /* 146 | * Helper methods 147 | */ 148 | 149 | @JsonIgnore 150 | public static boolean isMediaType(ContentType mediaType) { 151 | boolean isResolveResultMimeTypeEquals = CONTENT_TYPE.getMimeType().equals(mediaType.getMimeType()); 152 | boolean isResolveResultProfileEquals = CONTENT_TYPE.getParameter("profile").equals(mediaType.getParameter("profile")); 153 | return isResolveResultMimeTypeEquals && isResolveResultProfileEquals; 154 | } 155 | 156 | /* 157 | * Object methods 158 | */ 159 | 160 | @Override 161 | public String toString() { 162 | return this.toJson(); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/result/Result.java: -------------------------------------------------------------------------------- 1 | package uniresolver.result; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | 5 | import java.io.IOException; 6 | import java.net.URI; 7 | import java.util.ArrayList; 8 | import java.util.LinkedHashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public interface Result { 13 | 14 | /* 15 | * Field methods 16 | */ 17 | 18 | @JsonIgnore 19 | public Map getFunctionMetadata(); 20 | 21 | @JsonIgnore 22 | public byte[] getFunctionContent() throws IOException; 23 | 24 | @JsonIgnore 25 | public Map getFunctionContentMetadata(); 26 | 27 | /* 28 | * Serialization 29 | */ 30 | 31 | @JsonIgnore 32 | public Map toMap(); 33 | 34 | @JsonIgnore 35 | public String toJson(); 36 | 37 | @JsonIgnore 38 | public URI getDefaultContext(); 39 | 40 | @JsonIgnore 41 | public boolean isComplete(); 42 | 43 | /* 44 | * Content type methods 45 | */ 46 | 47 | @JsonIgnore 48 | default public String getContentType() { 49 | return (String) this.getFunctionMetadata().get("contentType"); 50 | } 51 | 52 | @JsonIgnore 53 | default public void setContentType(String contentType) { 54 | if (contentType != null) 55 | this.getFunctionMetadata().put("contentType", contentType); 56 | else 57 | this.getFunctionMetadata().remove("contentType"); 58 | } 59 | 60 | /* 61 | * Error methods 62 | */ 63 | 64 | @JsonIgnore 65 | default public boolean isErrorResult() { 66 | return this.getError() != null; 67 | } 68 | 69 | @JsonIgnore 70 | default public String getError() { 71 | if (this.getFunctionMetadata() == null) throw new NullPointerException(); 72 | return (String) this.getFunctionMetadata().get("error"); 73 | } 74 | 75 | @JsonIgnore 76 | default public void setError(String error) { 77 | if (this.getFunctionMetadata() == null) throw new NullPointerException(); 78 | if (error != null) 79 | this.getFunctionMetadata().put("error", error); 80 | else 81 | this.getFunctionMetadata().remove("error"); 82 | } 83 | 84 | @JsonIgnore 85 | default public String getErrorMessage() { 86 | if (this.getFunctionMetadata() == null) throw new NullPointerException(); 87 | return (String) this.getFunctionMetadata().get("errorMessage"); 88 | } 89 | 90 | @JsonIgnore 91 | default public void setErrorMessage(String error) { 92 | if (this.getFunctionMetadata() == null) throw new NullPointerException(); 93 | if (error != null) 94 | this.getFunctionMetadata().put("errorMessage", error); 95 | else 96 | this.getFunctionMetadata().remove("errorMessage"); 97 | } 98 | 99 | /* 100 | * Problem/warning methods 101 | */ 102 | 103 | @JsonIgnore 104 | default public boolean hasWarnings() { 105 | return this.getWarnings() != null && !this.getWarnings().isEmpty(); 106 | } 107 | 108 | @JsonIgnore 109 | default public List> getWarnings() { 110 | return this.getFunctionMetadata() == null ? null : (List>) this.getFunctionMetadata().get("warnings"); 111 | } 112 | 113 | @JsonIgnore 114 | default public void addWarning(String message, Map warningMetadata) { 115 | if (message == null) throw new NullPointerException(); 116 | if (this.getFunctionMetadata() == null) throw new NullPointerException(); 117 | List> warnings = (List>) this.getFunctionMetadata().get("warnings"); 118 | if (warnings == null) { warnings = new ArrayList<>(); this.getFunctionMetadata().put("warnings", warnings); } 119 | Map warning = new LinkedHashMap<>(); 120 | if (message != null) warning.put("message", message); 121 | if (warningMetadata != null) warning.putAll(warningMetadata); 122 | warnings.add(warning); 123 | } 124 | 125 | @JsonIgnore 126 | default public void addWarning(String message) { 127 | this.addWarning(message, null); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/w3c/DIDResolver.java: -------------------------------------------------------------------------------- 1 | package uniresolver.w3c; 2 | 3 | import uniresolver.ResolutionException; 4 | import uniresolver.result.ResolveResult; 5 | 6 | import java.util.Map; 7 | 8 | public interface DIDResolver { 9 | 10 | public ResolveResult resolve(String didString, Map resolutionOptions) throws ResolutionException; 11 | } 12 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/w3c/DIDURLDereferencer.java: -------------------------------------------------------------------------------- 1 | package uniresolver.w3c; 2 | 3 | import uniresolver.DereferencingException; 4 | import uniresolver.ResolutionException; 5 | import uniresolver.result.DereferenceResult; 6 | 7 | import java.util.Map; 8 | 9 | public interface DIDURLDereferencer { 10 | 11 | public DereferenceResult dereference(String didUrlString, Map dereferenceOptions) throws DereferencingException, ResolutionException; 12 | } 13 | -------------------------------------------------------------------------------- /uni-resolver-local/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /uni-resolver-local/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | uni-resolver-local 5 | jar 6 | uni-resolver-local 7 | 8 | 9 | decentralized-identity 10 | uni-resolver 11 | 0.33-SNAPSHOT 12 | 13 | 14 | 15 | 16 | decentralized-identity 17 | uni-resolver-core 18 | 19 | 20 | decentralized-identity 21 | uni-resolver-driver 22 | 23 | 24 | decentralized-identity 25 | uni-resolver-driver-http 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /uni-resolver-local/src/main/java/uniresolver/local/configuration/LocalUniResolverConfigurator.java: -------------------------------------------------------------------------------- 1 | package uniresolver.local.configuration; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import uniresolver.driver.Driver; 7 | import uniresolver.driver.http.HttpDriver; 8 | import uniresolver.local.LocalUniResolver; 9 | 10 | import java.io.FileReader; 11 | import java.io.IOException; 12 | import java.io.Reader; 13 | import java.net.URI; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class LocalUniResolverConfigurator { 19 | 20 | private static final Logger log = LoggerFactory.getLogger(LocalUniResolverConfigurator.class); 21 | 22 | private final static ObjectMapper objectMapper = new ObjectMapper(); 23 | 24 | public static void configureLocalUniResolver(String filePath, LocalUniResolver localUniResolver) throws IOException { 25 | 26 | List drivers = new ArrayList<>(); 27 | 28 | try (Reader reader = new FileReader(filePath)) { 29 | 30 | Map jsonRoot = objectMapper.readValue(reader, Map.class); 31 | List> jsonDrivers = (List>) jsonRoot.get("drivers"); 32 | 33 | for (Map jsonDriver : jsonDrivers) { 34 | 35 | String pattern = jsonDriver.containsKey("pattern") ? (String) jsonDriver.get("pattern") : null; 36 | String url = jsonDriver.containsKey("url") ? (String) jsonDriver.get("url") : null; 37 | String propertiesEndpoint = jsonDriver.containsKey("propertiesEndpoint") ? (String) jsonDriver.get("propertiesEndpoint") : null; 38 | String supportsOptions = jsonDriver.containsKey("supportsOptions") ? (String) jsonDriver.get("supportsOptions") : null; 39 | String supportsDereference = jsonDriver.containsKey("supportsDereference") ? (String) jsonDriver.get("supportsDereference") : null; 40 | String acceptHeaderValue = jsonDriver.containsKey("acceptHeaderValue") ? (String) jsonDriver.get("acceptHeaderValue") : null; 41 | List testIdentifiers = jsonDriver.containsKey("testIdentifiers") ? (List) jsonDriver.get("testIdentifiers") : null; 42 | Map traits = jsonDriver.containsKey("traits") ? (Map) jsonDriver.get("traits") : null; 43 | 44 | if (pattern == null) throw new IllegalArgumentException("Missing 'pattern' entry in driver configuration."); 45 | if (url == null) throw new IllegalArgumentException("Missing 'url' entry in driver configuration."); 46 | 47 | // construct HTTP driver 48 | 49 | HttpDriver driver = new HttpDriver(); 50 | driver.setPattern(pattern); 51 | 52 | if (url.contains("$1") || url.contains("$2")) { 53 | driver.setResolveUri(url); 54 | driver.setPropertiesUri((URI) null); 55 | } else { 56 | if (! url.endsWith("/")) url = url + "/"; 57 | driver.setResolveUri(url + "1.0/identifiers/"); 58 | if ("true".equals(propertiesEndpoint)) driver.setPropertiesUri(url + "1.0/properties"); 59 | } 60 | 61 | if (supportsOptions != null) driver.setSupportsOptions(Boolean.parseBoolean(supportsOptions)); 62 | if (supportsDereference != null) driver.setSupportsDereference(Boolean.parseBoolean(supportsDereference)); 63 | if (acceptHeaderValue != null) driver.setAcceptHeaderValue(acceptHeaderValue); 64 | if (testIdentifiers != null) driver.setTestIdentifiers(testIdentifiers); 65 | if (traits != null) driver.setTraits(traits); 66 | 67 | // done 68 | 69 | drivers.add(driver); 70 | if (log.isInfoEnabled()) log.info("Added driver for pattern '" + pattern + "' at " + driver.getResolveUri() + " (" + driver.getPropertiesUri() + ")"); 71 | } 72 | } 73 | 74 | // done 75 | 76 | localUniResolver.setDrivers(drivers); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /uni-resolver-local/src/main/java/uniresolver/local/extensions/DereferencerExtension.java: -------------------------------------------------------------------------------- 1 | package uniresolver.local.extensions; 2 | 3 | import foundation.identity.did.DIDURL; 4 | import uniresolver.DereferencingException; 5 | import uniresolver.ResolutionException; 6 | import uniresolver.local.LocalUniDereferencer; 7 | import uniresolver.result.DereferenceResult; 8 | import uniresolver.result.ResolveResult; 9 | 10 | import java.lang.annotation.ElementType; 11 | import java.lang.annotation.Retention; 12 | import java.lang.annotation.RetentionPolicy; 13 | import java.lang.annotation.Target; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | public interface DereferencerExtension { 18 | 19 | @Retention(RetentionPolicy.RUNTIME) 20 | @Target(ElementType.TYPE) 21 | @interface ExtensionStage { 22 | String value(); 23 | } 24 | 25 | @FunctionalInterface 26 | interface ExtensionFunction { 27 | ExtensionStatus apply(E extension) throws ResolutionException, DereferencingException; 28 | } 29 | 30 | @ExtensionStage("beforeDereference") 31 | interface BeforeDereferenceDereferencerExtension extends DereferencerExtension { 32 | default ExtensionStatus beforeDereference(DIDURL didUrl, Map dereferenceOptions, DereferenceResult dereferenceResult, Map executionState, LocalUniDereferencer localUniDereferencer) throws ResolutionException, DereferencingException { 33 | return null; 34 | } 35 | } 36 | 37 | @ExtensionStage("dereferencePrimary") 38 | interface DereferencePrimaryDereferencerExtension extends DereferencerExtension { 39 | default ExtensionStatus dereferencePrimary(DIDURL didUrlWithoutFragment, Map dereferenceOptions, ResolveResult resolveResult, DereferenceResult dereferenceResult, Map executionState, LocalUniDereferencer localUniDereferencer) throws ResolutionException, DereferencingException { 40 | return null; 41 | } 42 | } 43 | 44 | @ExtensionStage("dereferenceSecondary") 45 | interface DereferenceSecondaryDereferencerExtension extends DereferencerExtension { 46 | default ExtensionStatus dereferenceSecondary(DIDURL didUrlWithoutFragment, String didUrlFragment, Map dereferenceOptions, DereferenceResult dereferenceResult, Map executionState, LocalUniDereferencer localUniDereferencer) throws ResolutionException, DereferencingException { 47 | return null; 48 | } 49 | } 50 | 51 | @ExtensionStage("afterDereference") 52 | interface AfterDereferenceDereferencerExtension extends DereferencerExtension { 53 | default ExtensionStatus afterDereference(DIDURL didUrl, Map dereferenceOptions, DereferenceResult dereferenceResult, Map executionState, LocalUniDereferencer localUniDereferencer) throws ResolutionException, DereferencingException { 54 | return null; 55 | } 56 | } 57 | 58 | abstract class AbstractDereferencerExtension implements BeforeDereferenceDereferencerExtension, DereferencePrimaryDereferencerExtension, DereferenceSecondaryDereferencerExtension, AfterDereferenceDereferencerExtension { 59 | } 60 | 61 | static List extensionClassNames(List extensions) { 62 | return extensions.stream().map(e -> e.getClass().getSimpleName()).toList(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /uni-resolver-local/src/main/java/uniresolver/local/extensions/ExtensionStatus.java: -------------------------------------------------------------------------------- 1 | package uniresolver.local.extensions; 2 | 3 | public class ExtensionStatus { 4 | 5 | public static final ExtensionStatus DEFAULT = new ExtensionStatus(); 6 | 7 | public static final ExtensionStatus SKIP_BEFORE_RESOLVE = new ExtensionStatus(true, false, false, false, false, false, false); 8 | public static final ExtensionStatus SKIP_RESOLVE = new ExtensionStatus(false, true, false, false, false, false, false); 9 | public static final ExtensionStatus SKIP_AFTER_RESOLVE = new ExtensionStatus(false, false, true, false, false, false, false); 10 | public static final ExtensionStatus SKIP_BEFORE_DEREFERENCE = new ExtensionStatus(false, false, false, true, false, false, false); 11 | public static final ExtensionStatus SKIP_DEREFERENCE_PRIMARY = new ExtensionStatus(false, false, false, false, true, false, false); 12 | public static final ExtensionStatus SKIP_DEREFERENCE_SECONDARY = new ExtensionStatus(false, false, false, false, false, true, false); 13 | public static final ExtensionStatus SKIP_AFTER_DEREFERENCE = new ExtensionStatus(false, false, false, false, false, false, true); 14 | 15 | private boolean skipBeforeResolve; 16 | private boolean skipResolve; 17 | private boolean skipAfterResolve; 18 | private boolean skipBeforeDereference; 19 | private boolean skipDereferencePrimary; 20 | private boolean skipDereferenceSecondary; 21 | private boolean skipAfterDereference; 22 | 23 | public ExtensionStatus(boolean skipBeforeResolve, boolean skipResolve, boolean skipAfterResolve, boolean skipBeforeDereference, boolean skipDereferencePrimary, boolean skipDereferenceSecondary, boolean skipAfterDereference) { 24 | 25 | this.skipBeforeResolve = skipBeforeResolve; 26 | this.skipResolve = skipResolve; 27 | this.skipAfterResolve = skipAfterResolve; 28 | this.skipBeforeDereference = skipBeforeDereference; 29 | this.skipDereferencePrimary = skipDereferencePrimary; 30 | this.skipDereferenceSecondary = skipDereferenceSecondary; 31 | this.skipAfterDereference = skipAfterDereference; 32 | } 33 | 34 | public ExtensionStatus() { 35 | 36 | this(false, false, false, false, false, false, false); 37 | } 38 | 39 | public void or(ExtensionStatus extensionStatus) { 40 | 41 | if (extensionStatus == null) return; 42 | 43 | this.skipBeforeResolve |= extensionStatus.skipBeforeResolve; 44 | this.skipResolve |= extensionStatus.skipResolve; 45 | this.skipAfterResolve |= extensionStatus.skipAfterResolve; 46 | this.skipBeforeDereference |= extensionStatus.skipBeforeDereference; 47 | this.skipDereferencePrimary |= extensionStatus.skipDereferencePrimary; 48 | this.skipDereferenceSecondary |= extensionStatus.skipDereferenceSecondary; 49 | this.skipAfterDereference |= extensionStatus.skipAfterDereference; 50 | } 51 | 52 | public boolean skip(String extensionStage) { 53 | return switch (extensionStage) { 54 | case "beforeResolve" -> this.skipBeforeResolve; 55 | case "resolve" -> this.skipResolve; 56 | case "afterResolve" -> this.skipAfterResolve; 57 | case "beforeDereference" -> this.skipBeforeDereference; 58 | case "dereferencePrimary" -> this.skipDereferencePrimary; 59 | case "dereferenceSecondary" -> this.skipDereferenceSecondary; 60 | case "afterDereference" -> this.skipAfterDereference; 61 | default -> throw new IllegalStateException("Unexpected extension stage: " + extensionStage); 62 | }; 63 | } 64 | 65 | public boolean skipBeforeResolve() { 66 | return this.skipBeforeResolve; 67 | } 68 | 69 | public boolean skipResolve() { 70 | return this.skipResolve; 71 | } 72 | 73 | public boolean skipAfterResolve() { 74 | return this.skipAfterResolve; 75 | } 76 | 77 | public boolean skipBeforeDereference() { 78 | return skipBeforeDereference; 79 | } 80 | 81 | public boolean skipDereferencePrimary() { 82 | return skipDereferencePrimary; 83 | } 84 | 85 | public boolean skipDereferenceSecondary() { 86 | return skipDereferenceSecondary; 87 | } 88 | 89 | public boolean skipAfterDereference() { 90 | return skipAfterDereference; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /uni-resolver-local/src/main/java/uniresolver/local/extensions/ResolverExtension.java: -------------------------------------------------------------------------------- 1 | package uniresolver.local.extensions; 2 | 3 | import foundation.identity.did.DID; 4 | import uniresolver.ResolutionException; 5 | import uniresolver.local.LocalUniResolver; 6 | import uniresolver.result.ResolveResult; 7 | 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public interface ResolverExtension { 16 | 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Target(ElementType.TYPE) 19 | @interface ExtensionStage { 20 | String value(); 21 | } 22 | 23 | @FunctionalInterface 24 | interface ExtensionFunction { 25 | ExtensionStatus apply(E extension) throws ResolutionException; 26 | } 27 | 28 | @ExtensionStage("beforeResolve") 29 | interface BeforeResolveResolverExtension extends ResolverExtension { 30 | default ExtensionStatus beforeResolve(DID did, Map resolutionOptions, ResolveResult resolveResult, Map executionState, LocalUniResolver localUniResolver) throws ResolutionException { 31 | return null; 32 | } 33 | } 34 | 35 | @ExtensionStage("afterResolve") 36 | interface AfterResolveResolverExtension extends ResolverExtension { 37 | default ExtensionStatus afterResolve(DID did, Map resolutionOptions, ResolveResult resolveResult, Map executionState, LocalUniResolver localUniResolver) throws ResolutionException { 38 | return null; 39 | } 40 | } 41 | 42 | abstract class AbstractResolverExtension implements BeforeResolveResolverExtension, AfterResolveResolverExtension { 43 | } 44 | 45 | static List extensionClassNames(List extensions) { 46 | return extensions.stream().map(e -> e.getClass().getSimpleName()).toList(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /uni-resolver-local/src/main/java/uniresolver/local/extensions/impl/DIDDocumentExtension.java: -------------------------------------------------------------------------------- 1 | package uniresolver.local.extensions.impl; 2 | 3 | import foundation.identity.did.DIDURL; 4 | import foundation.identity.did.representations.Representations; 5 | import foundation.identity.did.representations.production.RepresentationProducer; 6 | import foundation.identity.did.representations.production.RepresentationProducerDIDCBOR; 7 | import foundation.identity.did.representations.production.RepresentationProducerDIDJSON; 8 | import foundation.identity.did.representations.production.RepresentationProducerDIDJSONLD; 9 | import org.apache.http.entity.ContentType; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import uniresolver.DereferencingException; 13 | import uniresolver.ResolutionException; 14 | import uniresolver.local.LocalUniDereferencer; 15 | import uniresolver.local.extensions.DereferencerExtension; 16 | import uniresolver.local.extensions.ExtensionStatus; 17 | import uniresolver.result.DereferenceResult; 18 | import uniresolver.result.ResolveResult; 19 | 20 | import java.io.IOException; 21 | import java.util.Map; 22 | 23 | public class DIDDocumentExtension implements DereferencerExtension.DereferencePrimaryDereferencerExtension { 24 | 25 | private static final Logger log = LoggerFactory.getLogger(DIDDocumentExtension.class); 26 | 27 | @Override 28 | public ExtensionStatus dereferencePrimary(DIDURL didUrlWithoutFragment, Map dereferenceOptions, ResolveResult resolveResult, DereferenceResult dereferenceResult, Map executionState, LocalUniDereferencer localUniDereferencer) throws ResolutionException, DereferencingException { 29 | 30 | // check inputs 31 | 32 | String accept = (String) dereferenceOptions.get("accept"); 33 | 34 | if (didUrlWithoutFragment.getPath() != null) { 35 | if (log.isDebugEnabled()) log.debug("Skipping (DID URL has path)."); 36 | return null; 37 | } 38 | 39 | if (resolveResult == null) { 40 | if (log.isDebugEnabled()) log.debug("Skipping (no resolve result)."); 41 | return null; 42 | } 43 | 44 | if (log.isInfoEnabled()) log.info("Executing dereferencePrimary() with extension " + this.getClass().getName()); 45 | 46 | // dereference 47 | 48 | if ("*/*".equals(accept)) accept = Representations.DEFAULT_MEDIA_TYPE; 49 | else if ("application/ld+json".equals(accept)) accept = RepresentationProducerDIDJSONLD.MEDIA_TYPE; 50 | else if ("application/json".equals(accept)) accept = RepresentationProducerDIDJSON.MEDIA_TYPE; 51 | else if ("application/cbor".equals(accept)) accept = RepresentationProducerDIDCBOR.MEDIA_TYPE; 52 | if (! Representations.isProducibleMediaType(accept)) { 53 | throw new DereferencingException(DereferencingException.ERROR_CONTENTTYPENOTSUPPORTED, "Content type not supported: " + accept); 54 | } 55 | 56 | if (log.isDebugEnabled()) log.debug("Dereferencing DID URL that has no path (assuming DID document): " + didUrlWithoutFragment); 57 | 58 | RepresentationProducer representationProducer; 59 | byte[] content; 60 | try { 61 | representationProducer = Representations.getProducer(accept); 62 | content = representationProducer.produce(resolveResult.getDidDocument()); 63 | } catch (IOException ex) { 64 | throw new DereferencingException(DereferencingException.ERROR_CONTENTTYPENOTSUPPORTED, "Cannot produce DID document: " + ex.getMessage(), ex); 65 | } 66 | 67 | if (log.isDebugEnabled()) log.debug("Dereferenced DID document with content type " + resolveResult.getContentType() + " using producer for " + representationProducer.getMediaType()); 68 | 69 | // set dereference result 70 | 71 | dereferenceResult.setContentType(representationProducer.getMediaType()); 72 | dereferenceResult.setContent(content); 73 | dereferenceResult.setContentMetadata(resolveResult.getDidDocumentMetadata()); 74 | 75 | // done 76 | 77 | return ExtensionStatus.SKIP_DEREFERENCE_PRIMARY; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /uni-resolver-local/src/main/java/uniresolver/local/extensions/impl/DummyDereferencerExtension.java: -------------------------------------------------------------------------------- 1 | package uniresolver.local.extensions.impl; 2 | 3 | import foundation.identity.did.DIDURL; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import uniresolver.DereferencingException; 7 | import uniresolver.ResolutionException; 8 | import uniresolver.local.LocalUniDereferencer; 9 | import uniresolver.local.extensions.DereferencerExtension; 10 | import uniresolver.local.extensions.DereferencerExtension.AbstractDereferencerExtension; 11 | import uniresolver.local.extensions.ExtensionStatus; 12 | import uniresolver.result.DereferenceResult; 13 | 14 | import java.util.Map; 15 | 16 | public class DummyDereferencerExtension extends AbstractDereferencerExtension implements DereferencerExtension { 17 | 18 | private static final Logger log = LoggerFactory.getLogger(DummyDereferencerExtension.class); 19 | 20 | @Override 21 | public ExtensionStatus afterDereference(DIDURL didUrl, Map dereferenceOptions, DereferenceResult dereferenceResult, Map executionState, LocalUniDereferencer localUniDereferencer) throws ResolutionException, DereferencingException { 22 | 23 | if (log.isDebugEnabled()) log.debug("Dummy extension called!"); 24 | return ExtensionStatus.DEFAULT; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /uni-resolver-local/src/main/java/uniresolver/local/extensions/impl/DummyResolverExtension.java: -------------------------------------------------------------------------------- 1 | package uniresolver.local.extensions.impl; 2 | 3 | import foundation.identity.did.DID; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import uniresolver.ResolutionException; 7 | import uniresolver.local.LocalUniResolver; 8 | import uniresolver.local.extensions.ExtensionStatus; 9 | import uniresolver.local.extensions.ResolverExtension; 10 | import uniresolver.local.extensions.ResolverExtension.AbstractResolverExtension; 11 | import uniresolver.result.ResolveResult; 12 | 13 | import java.util.Map; 14 | 15 | public class DummyResolverExtension extends AbstractResolverExtension implements ResolverExtension { 16 | 17 | private static final Logger log = LoggerFactory.getLogger(DummyResolverExtension.class); 18 | 19 | @Override 20 | public ExtensionStatus afterResolve(DID did, Map resolutionOptions, ResolveResult resolveResult, Map executionState, LocalUniResolver localUniResolver) throws ResolutionException { 21 | 22 | if (log.isDebugEnabled()) log.debug("Dummy extension called!"); 23 | return ExtensionStatus.DEFAULT; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /uni-resolver-local/src/main/java/uniresolver/local/extensions/util/ExecutionStateUtil.java: -------------------------------------------------------------------------------- 1 | package uniresolver.local.extensions.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import uniresolver.local.extensions.DereferencerExtension; 6 | import uniresolver.local.extensions.ResolverExtension; 7 | 8 | import java.util.ArrayList; 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class ExecutionStateUtil { 14 | 15 | private static final String RESOLVER_EXTENSION_STAGES = "resolverExtensionStages"; 16 | private static final String DEREFERENCER_EXTENSION_STAGES = "dereferencerExtensionStages"; 17 | 18 | private static final Logger log = LoggerFactory.getLogger(ExecutionStateUtil.class); 19 | 20 | public static void addResolverExtensionStage(Map executionState, Class extensionClass, ResolverExtension resolverExtension) { 21 | 22 | String extensionStage = extensionClass.getAnnotation(ResolverExtension.ExtensionStage.class).value(); 23 | String extensionName = resolverExtension.getClass().getSimpleName(); 24 | if (log.isDebugEnabled()) log.debug("Add resolver extension stage: " + extensionStage + " / " + extensionName); 25 | 26 | LinkedHashMap> executionStateStages = (LinkedHashMap>) executionState.computeIfAbsent(RESOLVER_EXTENSION_STAGES, f -> new LinkedHashMap>()); 27 | List executionStateStagesExtensions = executionStateStages.computeIfAbsent(extensionStage, f -> new ArrayList<>()); 28 | executionStateStagesExtensions.add(extensionName); 29 | } 30 | 31 | public static boolean checkResolverExtensionStage(Map executionState, Class extensionClass, ResolverExtension resolverExtension) { 32 | 33 | String extensionStage = extensionClass.getAnnotation(ResolverExtension.ExtensionStage.class).value(); 34 | String extensionName = resolverExtension.getClass().getSimpleName(); 35 | if (log.isDebugEnabled()) log.debug("Check resolver extension stage: " + extensionStage + " / " + extensionName); 36 | 37 | LinkedHashMap> executionStateStages = (LinkedHashMap>) executionState.get(RESOLVER_EXTENSION_STAGES); 38 | if (executionStateStages == null) return false; 39 | 40 | List executionStateStagesExtensions = executionStateStages.get(extensionStage); 41 | if (executionStateStagesExtensions == null) return false; 42 | 43 | return executionStateStagesExtensions.contains(extensionName); 44 | } 45 | 46 | public static void addDereferencerExtensionStage(Map executionState, Class extensionClass, DereferencerExtension dereferencerExtension) { 47 | 48 | String extensionStage = extensionClass.getAnnotation(DereferencerExtension.ExtensionStage.class).value(); 49 | String extensionName = dereferencerExtension.getClass().getSimpleName(); 50 | if (log.isDebugEnabled()) log.debug("Add dereferencer extension stage: " + extensionStage + " / " + extensionName); 51 | 52 | LinkedHashMap> executionStateStages = (LinkedHashMap>) executionState.computeIfAbsent(DEREFERENCER_EXTENSION_STAGES, f -> new LinkedHashMap>()); 53 | List executionStateStagesExtensions = executionStateStages.computeIfAbsent(extensionStage, f -> new ArrayList<>()); 54 | executionStateStagesExtensions.add(extensionName); 55 | } 56 | 57 | public static boolean checkDereferencerExtensionStage(Map executionState, Class extensionClass, DereferencerExtension dereferencerExtension) { 58 | 59 | String extensionStage = extensionClass.getAnnotation(DereferencerExtension.ExtensionStage.class).value(); 60 | String extensionName = dereferencerExtension.getClass().getSimpleName(); 61 | if (log.isDebugEnabled()) log.debug("Check dereferencer extension stage: " + extensionStage + " / " + extensionName); 62 | 63 | LinkedHashMap> executionStateStages = (LinkedHashMap>) executionState.get(DEREFERENCER_EXTENSION_STAGES); 64 | if (executionStateStages == null) return false; 65 | 66 | List executionStateStagesExtensions = executionStateStages.get(extensionStage); 67 | if (executionStateStagesExtensions == null) return false; 68 | 69 | return executionStateStagesExtensions.contains(extensionName); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /uni-resolver-web/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /uni-resolver-web/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for universalresolver/uni-resolver-web 2 | 3 | FROM maven:3-eclipse-temurin-17-focal AS build 4 | MAINTAINER Markus Sabadello 5 | 6 | # build uni-resolver-web 7 | 8 | ADD . /opt/universal-resolver 9 | RUN cd /opt/universal-resolver && mvn clean install -N 10 | RUN cd /opt/universal-resolver/uni-resolver-core && mvn clean install -N 11 | RUN cd /opt/universal-resolver/driver && mvn clean install -N 12 | RUN cd /opt/universal-resolver/driver-http && mvn clean install -N 13 | RUN cd /opt/universal-resolver/uni-resolver-local && mvn clean install -N 14 | RUN cd /opt/universal-resolver/uni-resolver-web && mvn clean package -N 15 | 16 | # build image 17 | 18 | FROM eclipse-temurin:17-jre-alpine 19 | # For amd64 architecture use amd64/eclipse-temurin:17-jre-alpine 20 | 21 | MAINTAINER Markus Sabadello 22 | 23 | WORKDIR /opt/universal-resolver/uni-resolver-web/ 24 | 25 | COPY --from=build /opt/universal-resolver/uni-resolver-web/target/*-exec.jar ./ 26 | 27 | ENV uniresolver_web_spring_profiles_active=default 28 | 29 | # done 30 | 31 | EXPOSE 8080 32 | CMD java -jar *.jar 33 | -------------------------------------------------------------------------------- /uni-resolver-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | uni-resolver-web 5 | jar 6 | uni-resolver-web 7 | 8 | 9 | decentralized-identity 10 | uni-resolver 11 | 0.33-SNAPSHOT 12 | 13 | 14 | 15 | 3.4.4 16 | 1.14.5 17 | 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-maven-plugin 24 | ${spring.boot.version} 25 | 26 | uniresolver.web.WebUniResolverApplication 27 | 28 | 29 | 30 | 31 | repackage 32 | 33 | 34 | exec 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-dependencies 47 | ${spring.boot.version} 48 | pom 49 | import 50 | 51 | 52 | io.micrometer 53 | micrometer-registry-prometheus 54 | ${io.micrometer.micrometer-registry-prometheus.version} 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-web 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-logging 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-tomcat 71 | 72 | 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-starter-jetty 77 | 78 | 79 | decentralized-identity 80 | uni-resolver-local 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-starter-log4j2 85 | 86 | 87 | io.micrometer 88 | micrometer-registry-prometheus 89 | runtime 90 | 91 | 92 | org.springframework.boot 93 | spring-boot-starter-actuator 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/WebUniResolver.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web; 2 | 3 | import jakarta.servlet.ServletException; 4 | import jakarta.servlet.annotation.WebServlet; 5 | import jakarta.servlet.http.HttpServlet; 6 | import jakarta.servlet.http.HttpServletRequest; 7 | import jakarta.servlet.http.HttpServletResponse; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Qualifier; 10 | import org.springframework.lang.NonNull; 11 | import org.springframework.web.HttpRequestHandler; 12 | import uniresolver.DereferencingException; 13 | import uniresolver.ResolutionException; 14 | import uniresolver.UniDereferencer; 15 | import uniresolver.UniResolver; 16 | import uniresolver.result.DereferenceResult; 17 | import uniresolver.result.ResolveResult; 18 | 19 | import java.io.IOException; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.Set; 23 | 24 | @WebServlet 25 | public abstract class WebUniResolver extends HttpServlet implements HttpRequestHandler, UniDereferencer, UniResolver { 26 | 27 | @Autowired 28 | @Qualifier("UniDereferencer") 29 | private UniDereferencer uniDereferencer; 30 | 31 | @Autowired 32 | @Qualifier("UniResolver") 33 | private UniResolver uniResolver; 34 | 35 | protected WebUniResolver() { 36 | 37 | super(); 38 | } 39 | 40 | @Override 41 | public void handleRequest(HttpServletRequest request, @NonNull HttpServletResponse response) throws ServletException, IOException { 42 | if ("GET".equals(request.getMethod())) this.doGet(request, response); 43 | if ("POST".equals(request.getMethod())) this.doPost(request, response); 44 | if ("PUT".equals(request.getMethod())) this.doPut(request, response); 45 | if ("DELETE".equals(request.getMethod())) this.doDelete(request, response); 46 | if ("OPTIONS".equals(request.getMethod())) this.doOptions(request, response); 47 | } 48 | 49 | @Override 50 | protected void doOptions(HttpServletRequest request, HttpServletResponse response) { 51 | response.setHeader("Access-Control-Allow-Origin", "*"); 52 | response.setHeader("Access-Control-Allow-Headers", "Accept, Content-Type"); 53 | response.setStatus(HttpServletResponse.SC_OK); 54 | } 55 | 56 | @Override 57 | public ResolveResult resolve(String didString, Map resolutionOptions) throws ResolutionException { 58 | return this.getUniResolver() == null ? null : this.getUniResolver().resolve(didString, resolutionOptions); 59 | } 60 | 61 | @Override 62 | public Map> properties() throws ResolutionException { 63 | return this.getUniResolver() == null ? null : this.getUniResolver().properties(); 64 | } 65 | 66 | @Override 67 | public Set methods() throws ResolutionException { 68 | return this.getUniResolver() == null ? null : this.getUniResolver().methods(); 69 | } 70 | 71 | @Override 72 | public Map> testIdentifiers() throws ResolutionException { 73 | return this.getUniResolver() == null ? null : this.getUniResolver().testIdentifiers(); 74 | } 75 | 76 | @Override 77 | public Map> traits() throws ResolutionException { 78 | return this.getUniResolver() == null ? null : this.getUniResolver().traits(); 79 | } 80 | 81 | @Override 82 | public DereferenceResult dereference(String didUrlString, Map dereferenceOptions) throws ResolutionException, DereferencingException { 83 | return this.getUniDereferencer() == null ? null : this.getUniDereferencer().dereference(didUrlString, dereferenceOptions); 84 | } 85 | 86 | /* 87 | * Getters and setters 88 | */ 89 | 90 | public UniDereferencer getUniDereferencer() { 91 | return this.uniDereferencer; 92 | } 93 | 94 | public void setUniDereferencer(UniDereferencer uniDereferencer) { 95 | this.uniDereferencer = uniDereferencer; 96 | } 97 | 98 | public UniResolver getUniResolver() { 99 | return this.uniResolver; 100 | } 101 | 102 | public void setUniResolver(UniResolver uniResolver) { 103 | this.uniResolver = uniResolver; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/WebUniResolverApplication.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.builder.SpringApplicationBuilder; 7 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 8 | import org.springframework.context.ApplicationContext; 9 | import org.springframework.context.ApplicationContextAware; 10 | import org.springframework.context.annotation.Bean; 11 | import uniresolver.local.LocalUniDereferencer; 12 | import uniresolver.local.LocalUniResolver; 13 | 14 | @SpringBootApplication 15 | public class WebUniResolverApplication extends SpringBootServletInitializer implements ApplicationContextAware { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(WebUniResolverApplication.class, args); 19 | } 20 | 21 | private ApplicationContext applicationContext; 22 | 23 | @Override 24 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 25 | return application.sources(WebUniResolverApplication.class); 26 | } 27 | 28 | @Bean(name = "UniResolver") 29 | public LocalUniResolver localUniResolver() { 30 | return new LocalUniResolver(); 31 | } 32 | 33 | @Bean(name = "UniDereferencer") 34 | public LocalUniDereferencer localUniDereferencer() { 35 | return new LocalUniDereferencer(this.applicationContext.getBean("UniResolver", LocalUniResolver.class)); 36 | } 37 | 38 | @Override 39 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 40 | this.applicationContext = applicationContext; 41 | } 42 | } -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/WebUniResolverWebServerFactoryCustomizer.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web; 2 | 3 | import org.eclipse.jetty.http.UriCompliance; 4 | import org.eclipse.jetty.server.Connector; 5 | import org.eclipse.jetty.server.HttpConfiguration; 6 | import org.eclipse.jetty.server.HttpConnectionFactory; 7 | import org.eclipse.jetty.server.Server; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; 11 | import org.springframework.boot.web.server.WebServerFactoryCustomizer; 12 | import org.springframework.stereotype.Component; 13 | import uniresolver.web.servlet.ResolveServlet; 14 | 15 | @Component 16 | public class WebUniResolverWebServerFactoryCustomizer implements WebServerFactoryCustomizer { 17 | 18 | private static final Logger log = LoggerFactory.getLogger(WebUniResolverWebServerFactoryCustomizer.class); 19 | 20 | @Override 21 | public void customize(JettyServletWebServerFactory factory) { 22 | factory.addServerCustomizers(this::customizeUriCompliance); 23 | } 24 | 25 | private void customizeUriCompliance(Server server) { 26 | if (log.isInfoEnabled()) log.info("Customizing URI compliance: " + server); 27 | for (Connector connector : server.getConnectors()) { 28 | if (log.isInfoEnabled()) log.info("Customizing connector: " + connector); 29 | connector.getConnectionFactories().stream() 30 | .filter(factory -> factory instanceof HttpConnectionFactory) 31 | .forEach(factory -> { 32 | HttpConfiguration httpConfig = ((HttpConnectionFactory) factory).getHttpConfiguration(); 33 | httpConfig.setUriCompliance(UriCompliance.UNSAFE); 34 | if (log.isInfoEnabled()) log.info("Set URI compliance: " + httpConfig); 35 | }); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/config/DriverConfigs.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.StringJoiner; 9 | 10 | @Configuration 11 | @ConfigurationProperties("uniresolver") 12 | public class DriverConfigs { 13 | 14 | private List drivers; 15 | 16 | public List getDrivers() { 17 | return drivers; 18 | } 19 | 20 | public void setDrivers(List drivers) { 21 | this.drivers = drivers; 22 | } 23 | 24 | public static class DriverConfig { 25 | 26 | private String pattern; 27 | private String url; 28 | private String propertiesEndpoint; 29 | private String supportsOptions; 30 | private String supportsDereference; 31 | private String acceptHeaderValue; 32 | private List testIdentifiers; 33 | private Map traits; 34 | 35 | public String getPattern() { 36 | return pattern; 37 | } 38 | 39 | public void setPattern(String value) { 40 | this.pattern = value; 41 | } 42 | 43 | public String getUrl() { 44 | return url; 45 | } 46 | 47 | public void setUrl(String value) { 48 | this.url = value; 49 | } 50 | 51 | public String getPropertiesEndpoint() { 52 | return propertiesEndpoint; 53 | } 54 | 55 | public void setPropertiesEndpoint(String value) { 56 | this.propertiesEndpoint = value; 57 | } 58 | 59 | public String getSupportsOptions() { 60 | return supportsOptions; 61 | } 62 | 63 | public void setSupportsOptions(String supportsOptions) { 64 | this.supportsOptions = supportsOptions; 65 | } 66 | 67 | public String getSupportsDereference() { 68 | return supportsDereference; 69 | } 70 | 71 | public void setSupportsDereference(String supportsDereference) { 72 | this.supportsDereference = supportsDereference; 73 | } 74 | 75 | public String getAcceptHeaderValue() { 76 | return acceptHeaderValue; 77 | } 78 | 79 | public void setAcceptHeaderValue(String acceptHeaderValue) { 80 | this.acceptHeaderValue = acceptHeaderValue; 81 | } 82 | 83 | public List getTestIdentifiers() { 84 | return testIdentifiers; 85 | } 86 | 87 | public void setTestIdentifiers(List value) { 88 | this.testIdentifiers = value; 89 | } 90 | 91 | public Map getTraits() { 92 | return traits; 93 | } 94 | 95 | public void setTraits(Map traits) { 96 | this.traits = traits; 97 | } 98 | 99 | @Override 100 | public String toString() { 101 | return "DriverConfig{" + 102 | "pattern='" + pattern + '\'' + 103 | ", url='" + url + '\'' + 104 | ", propertiesEndpoint='" + propertiesEndpoint + '\'' + 105 | ", supportsOptions='" + supportsOptions + '\'' + 106 | ", supportsDereference='" + supportsDereference + '\'' + 107 | ", acceptHeaderValue='" + acceptHeaderValue + '\'' + 108 | ", testIdentifiers=" + testIdentifiers + 109 | ", traits=" + traits + 110 | '}'; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/config/ServletMappings.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | @ConfigurationProperties("server.servlet.mappings") 8 | public class ServletMappings { 9 | 10 | private String properties; 11 | private String resolve; 12 | private String methods; 13 | private String testIdentifiers; 14 | private String traits; 15 | 16 | public String getProperties() { 17 | return properties; 18 | } 19 | 20 | public String getResolve() { 21 | return resolve; 22 | } 23 | 24 | public String getMethods() { 25 | return methods; 26 | } 27 | 28 | public String getTestIdentifiers() { 29 | return testIdentifiers; 30 | } 31 | 32 | public String getTraits() { 33 | return traits; 34 | } 35 | 36 | public void setProperties(String properties) { 37 | this.properties = properties; 38 | } 39 | 40 | public void setResolve(String resolve) { 41 | this.resolve = resolve; 42 | } 43 | 44 | public void setMethods(String methods) { 45 | this.methods = methods; 46 | } 47 | 48 | public void setTestIdentifiers(String testIdentifiers) { 49 | this.testIdentifiers = testIdentifiers; 50 | } 51 | 52 | public void setTraits(String traits) { 53 | this.traits = traits; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/config/WebAppConfig.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web.config; 2 | 3 | import jakarta.annotation.PostConstruct; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.web.servlet.ServletRegistrationBean; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import uniresolver.driver.Driver; 11 | import uniresolver.driver.http.HttpDriver; 12 | import uniresolver.local.LocalUniResolver; 13 | import uniresolver.web.servlet.*; 14 | 15 | import java.net.URI; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | @Configuration 21 | public class WebAppConfig { 22 | 23 | private static final Logger log = LogManager.getLogger(WebAppConfig.class); 24 | 25 | @Autowired 26 | private DriverConfigs driverConfigs; 27 | 28 | @Autowired 29 | private ServletMappings servletMappings; 30 | 31 | @Autowired 32 | private LocalUniResolver localUniResolver; 33 | 34 | @Bean(name = "ResolveServlet") 35 | public ResolveServlet resolveServlet() { 36 | return new ResolveServlet(); 37 | } 38 | 39 | @Bean 40 | public ServletRegistrationBean propertiesServletRegistrationBean() { 41 | return new ServletRegistrationBean<>(propertiesServlet(), fixWildcardPattern(servletMappings.getProperties())); 42 | } 43 | 44 | @Bean(name = "PropertiesServlet") 45 | public PropertiesServlet propertiesServlet() { 46 | return new PropertiesServlet(); 47 | } 48 | 49 | @Bean 50 | public ServletRegistrationBean resolveServletRegistrationBean() { 51 | return new ServletRegistrationBean<>(resolveServlet(), fixWildcardPattern(servletMappings.getResolve())); 52 | } 53 | 54 | @Bean(name = "MethodServlet") 55 | public MethodsServlet methodsServlet() { 56 | return new MethodsServlet(); 57 | } 58 | 59 | @Bean 60 | public ServletRegistrationBean methodServletRegistrationBean() { 61 | return new ServletRegistrationBean<>(methodsServlet(), fixWildcardPattern(servletMappings.getMethods())); 62 | } 63 | 64 | @Bean(name = "TestIdentifiersServlet") 65 | public TestIdentifiersServlet testIdentifiersServlet() { 66 | return new TestIdentifiersServlet(); 67 | } 68 | 69 | @Bean 70 | public ServletRegistrationBean testIdentifiersServletRegistrationBean() { 71 | return new ServletRegistrationBean<>(testIdentifiersServlet(), servletMappings.getTestIdentifiers()); 72 | } 73 | 74 | @Bean(name = "TraitsServlet") 75 | public TraitsServlet traitsServlet() { 76 | return new TraitsServlet(); 77 | } 78 | 79 | @Bean 80 | public ServletRegistrationBean traitsServletRegistrationBean() { 81 | return new ServletRegistrationBean<>(traitsServlet(), servletMappings.getTraits()); 82 | } 83 | 84 | public static String fixWildcardPattern(String s) { 85 | if(s == null) return ""; 86 | if (s.endsWith("*")) return s; 87 | if (s.endsWith("/")) return s + "*"; 88 | return s + "/*"; 89 | } 90 | 91 | public static String normalizeUri(String s, boolean postSlash) { 92 | if (s == null) return null; 93 | String url = s; 94 | if (url.endsWith("*")) url = url.substring(0, url.length() - 1); 95 | 96 | URI uri = URI.create(url + "/").normalize(); 97 | 98 | return postSlash ? uri.toString() : uri.toString().substring(0, uri.toString().length() - 1); 99 | } 100 | 101 | public void configureLocalUniresolver(DriverConfigs driverConfigs, LocalUniResolver uniResolver) { 102 | 103 | List drivers = new ArrayList<>(); 104 | 105 | for (DriverConfigs.DriverConfig driverConfig : driverConfigs.getDrivers()) { 106 | 107 | String pattern = driverConfig.getPattern(); 108 | String url = driverConfig.getUrl(); 109 | String propertiesEndpoint = driverConfig.getPropertiesEndpoint(); 110 | String supportsOptions = driverConfig.getSupportsOptions(); 111 | String supportsDereference = driverConfig.getSupportsDereference(); 112 | String acceptHeaderValue = driverConfig.getAcceptHeaderValue(); 113 | List testIdentifiers = driverConfig.getTestIdentifiers(); 114 | Map traits = driverConfig.getTraits(); 115 | 116 | if (pattern == null) throw new IllegalArgumentException("Missing 'pattern' entry in driver configuration."); 117 | if (url == null) throw new IllegalArgumentException("Missing 'url' entry in driver configuration."); 118 | 119 | // construct HTTP driver 120 | 121 | HttpDriver driver = new HttpDriver(); 122 | driver.setPattern(pattern); 123 | 124 | if (url.contains("$1") || url.contains("$2")) { 125 | driver.setResolveUri(url); 126 | driver.setPropertiesUri((URI) null); 127 | } else { 128 | if (! url.endsWith("/")) url = url + "/"; 129 | driver.setResolveUri(normalizeUri((url + this.servletMappings.getResolve()), true)); 130 | if ("true".equals(propertiesEndpoint)) driver.setPropertiesUri(normalizeUri((url + this.servletMappings.getProperties()), false)); 131 | } 132 | 133 | if (supportsOptions != null) driver.setSupportsOptions(Boolean.parseBoolean(supportsOptions)); 134 | if (supportsDereference != null) driver.setSupportsDereference(Boolean.parseBoolean(supportsDereference)); 135 | if (acceptHeaderValue != null) driver.setAcceptHeaderValue(acceptHeaderValue); 136 | if (testIdentifiers != null) driver.setTestIdentifiers(testIdentifiers); 137 | if (traits != null) driver.setTraits(traits); 138 | 139 | // done 140 | 141 | drivers.add(driver); 142 | if (log.isInfoEnabled()) log.info("Added driver for pattern '" + driverConfig.getPattern() + "' at " + driver.getResolveUri() + " (" + driver.getPropertiesUri() + ")"); 143 | } 144 | 145 | uniResolver.setDrivers(drivers); 146 | } 147 | 148 | @PostConstruct 149 | private void initDrivers() { 150 | if (this.driverConfigs.getDrivers() != null) configureLocalUniresolver(this.driverConfigs, this.localUniResolver); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/servlet/MethodsServlet.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web.servlet; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import uniresolver.UniResolver; 9 | import uniresolver.web.WebUniResolver; 10 | 11 | import java.io.IOException; 12 | import java.util.Set; 13 | 14 | public class MethodsServlet extends WebUniResolver { 15 | 16 | protected static final Logger log = LoggerFactory.getLogger(MethodsServlet.class); 17 | 18 | private static final ObjectMapper objectMapper = new ObjectMapper(); 19 | 20 | @Override 21 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 22 | 23 | // read request 24 | 25 | request.setCharacterEncoding("UTF-8"); 26 | response.setCharacterEncoding("UTF-8"); 27 | 28 | if (log.isInfoEnabled()) log.info("Incoming request."); 29 | 30 | // execute the request 31 | 32 | Set methods; 33 | String methodsString; 34 | 35 | try { 36 | 37 | methods = this.methods(); 38 | methodsString = methods == null ? null : objectMapper.writeValueAsString(methods); 39 | } catch (Exception ex) { 40 | 41 | if (log.isWarnEnabled()) log.warn("Resolver reported: " + ex.getMessage(), ex); 42 | ServletUtil.sendResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Resolver reported: " + ex.getMessage()); 43 | return; 44 | } 45 | 46 | if (log.isInfoEnabled()) log.info("Methods: " + methods); 47 | 48 | // no result? 49 | 50 | if (methods == null) { 51 | 52 | ServletUtil.sendResponse(response, HttpServletResponse.SC_NOT_FOUND, "No methods."); 53 | return; 54 | } 55 | 56 | // write result 57 | 58 | ServletUtil.sendResponse(response, HttpServletResponse.SC_OK, UniResolver.METHODS_MEDIA_TYPE, methodsString); 59 | } 60 | } -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/servlet/PropertiesServlet.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web.servlet; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import uniresolver.UniResolver; 9 | import uniresolver.web.WebUniResolver; 10 | 11 | import java.io.IOException; 12 | import java.util.Map; 13 | 14 | public class PropertiesServlet extends WebUniResolver { 15 | 16 | protected static final Logger log = LoggerFactory.getLogger(PropertiesServlet.class); 17 | 18 | private static final ObjectMapper objectMapper = new ObjectMapper(); 19 | 20 | @Override 21 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 22 | 23 | // read request 24 | 25 | request.setCharacterEncoding("UTF-8"); 26 | response.setCharacterEncoding("UTF-8"); 27 | 28 | if (log.isInfoEnabled()) log.info("Incoming request."); 29 | 30 | // execute the request 31 | 32 | Map> properties; 33 | String propertiesString; 34 | 35 | try { 36 | 37 | properties = this.properties(); 38 | propertiesString = properties == null ? null : objectMapper.writeValueAsString(properties); 39 | } catch (Exception ex) { 40 | 41 | if (log.isWarnEnabled()) log.warn("Resolver reported: " + ex.getMessage(), ex); 42 | ServletUtil.sendResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Resolver reported: " + ex.getMessage()); 43 | return; 44 | } 45 | 46 | if (log.isInfoEnabled()) log.info("Properties: " + properties); 47 | 48 | // no result? 49 | 50 | if (properties == null) { 51 | 52 | ServletUtil.sendResponse(response, HttpServletResponse.SC_NOT_FOUND, "No properties."); 53 | return; 54 | } 55 | 56 | // write result 57 | 58 | ServletUtil.sendResponse(response, HttpServletResponse.SC_OK, UniResolver.PROPERTIES_MEDIA_TYPE, propertiesString); 59 | } 60 | } -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/servlet/ServletUtil.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web.servlet; 2 | 3 | import jakarta.servlet.http.HttpServletResponse; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.nio.charset.StandardCharsets; 10 | import java.util.Map; 11 | 12 | public class ServletUtil { 13 | 14 | private static final Logger log = LoggerFactory.getLogger(ServletUtil.class); 15 | 16 | public static void sendResponse(HttpServletResponse response, int status, Map headers, byte[] body) throws IOException { 17 | 18 | if (log.isInfoEnabled()) log.info("Sending response with status " + status + " and headers " + headers + " and body (" + (body == null ? null : body.length) + ")"); 19 | 20 | response.setStatus(status); 21 | if (headers != null) for (Map.Entry header : headers.entrySet()) response.setHeader(header.getKey(), header.getValue()); 22 | response.setHeader("Access-Control-Allow-Origin", "*"); 23 | 24 | if (body != null) { 25 | OutputStream outputStream = response.getOutputStream(); 26 | outputStream.write(body); 27 | outputStream.flush(); 28 | outputStream.close(); 29 | } 30 | 31 | response.flushBuffer(); 32 | } 33 | 34 | public static void sendResponse(HttpServletResponse response, int status, String contentType, byte[] body) throws IOException { 35 | sendResponse(response, status, Map.of("Content-Type", contentType), body); 36 | } 37 | 38 | public static void sendResponse(HttpServletResponse response, int status, String contentType, String body) throws IOException { 39 | sendResponse(response, status, Map.of("Content-Type", contentType), body == null ? null : body.getBytes(StandardCharsets.UTF_8)); 40 | } 41 | 42 | public static void sendResponse(HttpServletResponse response, int status, byte[] body) throws IOException { 43 | sendResponse(response, status, (Map) null, body); 44 | } 45 | 46 | public static void sendResponse(HttpServletResponse response, int status, String body) throws IOException { 47 | sendResponse(response, status, (Map) null, body == null ? null : body.getBytes(StandardCharsets.UTF_8)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/servlet/TestIdentifiersServlet.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web.servlet; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import uniresolver.UniResolver; 9 | import uniresolver.web.WebUniResolver; 10 | 11 | import java.io.IOException; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class TestIdentifiersServlet extends WebUniResolver { 16 | 17 | protected static final Logger log = LoggerFactory.getLogger(TestIdentifiersServlet.class); 18 | 19 | private static final ObjectMapper objectMapper = new ObjectMapper(); 20 | 21 | @Override 22 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 23 | 24 | // read request 25 | 26 | request.setCharacterEncoding("UTF-8"); 27 | response.setCharacterEncoding("UTF-8"); 28 | 29 | if (log.isInfoEnabled()) log.info("Incoming request."); 30 | 31 | // execute the request 32 | 33 | Map> testIdentifiers; 34 | String testIdentifiersString; 35 | 36 | try { 37 | 38 | testIdentifiers = this.testIdentifiers(); 39 | testIdentifiersString = testIdentifiers == null ? null : objectMapper.writeValueAsString(testIdentifiers); 40 | } catch (Exception ex) { 41 | 42 | if (log.isWarnEnabled()) log.warn("Resolver reported: " + ex.getMessage(), ex); 43 | ServletUtil.sendResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Resolver reported: " + ex.getMessage()); 44 | return; 45 | } 46 | 47 | if (log.isInfoEnabled()) log.info("Test identifiers: " + testIdentifiers); 48 | 49 | // no result? 50 | 51 | if (testIdentifiers == null) { 52 | 53 | ServletUtil.sendResponse(response, HttpServletResponse.SC_NOT_FOUND, "No test identifiers."); 54 | return; 55 | } 56 | 57 | // write result 58 | 59 | ServletUtil.sendResponse(response, HttpServletResponse.SC_OK, UniResolver.TEST_IDENTIFIER_MEDIA_TYPE, testIdentifiersString); 60 | } 61 | } -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/servlet/TraitsServlet.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web.servlet; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import uniresolver.UniResolver; 9 | import uniresolver.web.WebUniResolver; 10 | 11 | import java.io.IOException; 12 | import java.util.Map; 13 | 14 | public class TraitsServlet extends WebUniResolver { 15 | 16 | protected static final Logger log = LoggerFactory.getLogger(TraitsServlet.class); 17 | 18 | private static final ObjectMapper objectMapper = new ObjectMapper(); 19 | 20 | @Override 21 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 22 | 23 | // read request 24 | 25 | request.setCharacterEncoding("UTF-8"); 26 | response.setCharacterEncoding("UTF-8"); 27 | 28 | if (log.isInfoEnabled()) log.info("Incoming request."); 29 | 30 | // execute the request 31 | 32 | Map> traits; 33 | String traitsString; 34 | 35 | try { 36 | 37 | traits = this.traits(); 38 | traitsString = traits == null ? null : objectMapper.writeValueAsString(traits); 39 | } catch (Exception ex) { 40 | 41 | if (log.isWarnEnabled()) log.warn("Resolver reported: " + ex.getMessage(), ex); 42 | ServletUtil.sendResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Resolver reported: " + ex.getMessage()); 43 | return; 44 | } 45 | 46 | if (log.isInfoEnabled()) log.info("Traits: " + traits); 47 | 48 | // no result? 49 | 50 | if (traits == null) { 51 | 52 | ServletUtil.sendResponse(response, HttpServletResponse.SC_NOT_FOUND, "No traits."); 53 | return; 54 | } 55 | 56 | // write result 57 | 58 | ServletUtil.sendResponse(response, HttpServletResponse.SC_OK, UniResolver.TRAITS_MEDIA_TYPE, traitsString); 59 | } 60 | } -------------------------------------------------------------------------------- /uni-resolver-web/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | management: 2 | endpoints: 3 | web: 4 | exposure: 5 | include: '*' 6 | health: 7 | readinessState: 8 | enabled: 'true' 9 | livenessState: 10 | enabled: 'true' 11 | endpoint: 12 | health: 13 | probes: 14 | enabled: 'true' 15 | show-details: always 16 | status: 17 | http-mapping: 18 | down: '500' 19 | warning: '500' 20 | out_of_service: '503' 21 | -------------------------------------------------------------------------------- /uni-resolver-web/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /uni-resolver-web/src/test/resources/jetty-env.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /uni-resolver-web/src/test/resources/jetty.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /uni-resolver-web/src/test/resources/log4j2-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | --------------------------------------------------------------------------------