├── ci ├── run-did-test-suite │ ├── .gitignore │ ├── app │ │ ├── package.json │ │ ├── testserver-utils.js │ │ ├── local-files-utils.js │ │ ├── utils.js │ │ └── index.js │ ├── action.yml │ ├── entrypoint.sh │ ├── Dockerfile │ └── README.md ├── get-driver-status │ ├── .gitignore │ ├── app │ │ ├── requirements.txt │ │ └── get-driver-status.py │ ├── Dockerfile │ ├── action.yaml │ ├── entrypoint.sh │ └── README.md ├── deploy-k8s-aws │ ├── namespace │ │ ├── namespace-setup.yaml │ │ └── namespace-setup.sh │ ├── app-specs │ │ ├── secret-driver-did-sol.yaml │ │ ├── configmap-uni-resolver-frontend.yaml │ │ ├── configmap-driver-did-sol.yaml │ │ ├── deployment-uni-registrar-web.yaml │ │ ├── deployment-uni-registrar-frontend.yaml │ │ ├── deployment-uni-resolver-frontend.yaml │ │ └── deployment-uni-resolver-web.yaml │ ├── Dockerfile │ ├── scripts │ │ ├── entrypoint.sh │ │ ├── substitute-btcr-driver-values.py │ │ ├── driver-config.py │ │ ├── k8s-template.yaml │ │ └── driver-config.yaml │ └── README.md ├── docker-build-push │ ├── Dockerfile │ ├── README.md │ └── entrypoint.sh └── did-lint-check │ └── run.sh ├── examples ├── sovrin │ ├── lib │ │ └── .gitignore │ ├── localhost.txn │ ├── danube.txn │ └── mainnet.txn ├── .gitignore ├── src │ └── main │ │ ├── resources │ │ └── log4j2.properties │ │ └── java │ │ └── uniresolver │ │ └── examples │ │ ├── TestDriverDidBtcr.java │ │ ├── TestClientUniResolver.java │ │ ├── TestDriverDidSov.java │ │ ├── TestLocalUniResolver.java │ │ ├── TestLocalUniDereferencer.java │ │ └── w3ctestsuite │ │ ├── TestIdentifier.java │ │ ├── TestDIDResolution.java │ │ ├── TestDIDURLDereferencing.java │ │ └── TestSuiteUtil.java └── pom.xml ├── uni-resolver-core ├── openapi │ └── java-client-generated │ │ ├── .openapi-generator-ignore │ │ └── .gitignore ├── .gitignore ├── src │ └── main │ │ └── java │ │ └── uniresolver │ │ ├── w3c │ │ ├── DIDURLDereferencer.java │ │ └── DIDResolver.java │ │ ├── UniDereferencer.java │ │ ├── result │ │ ├── StreamResult.java │ │ ├── Result.java │ │ ├── ResolveDataModelResult.java │ │ └── ResolveRepresentationResult.java │ │ ├── UniResolver.java │ │ ├── DereferencingException.java │ │ └── ResolutionException.java └── pom.xml ├── docs ├── logo-dif.png ├── logo-ngi0pet.png ├── figures │ ├── overview.png │ ├── protocol.png │ ├── architecture.png │ └── aws-architecture.png ├── troubleshooting.md ├── design-goals.md ├── java-components.md ├── continuous-integration-and-delivery.md ├── dev-system.md └── branching-strategy.md ├── driver ├── .gitignore ├── src │ └── main │ │ └── java │ │ └── uniresolver │ │ └── driver │ │ ├── AbstractDriver.java │ │ ├── util │ │ └── MediaTypeUtil.java │ │ ├── servlet │ │ ├── InitServlet.java │ │ ├── PropertiesServlet.java │ │ ├── ServletUtil.java │ │ └── ResolveServlet.java │ │ └── Driver.java └── pom.xml ├── driver-http ├── .gitignore └── pom.xml ├── uni-resolver-client ├── .gitignore └── pom.xml ├── uni-resolver-local ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── uniresolver │ └── local │ ├── extensions │ ├── impl │ │ ├── DummyResolverExtension.java │ │ ├── DummyDereferencerExtension.java │ │ └── DIDDocumentExtension.java │ ├── ResolverExtension.java │ ├── DereferencerExtension.java │ ├── ExtensionStatus.java │ └── util │ │ └── ExecutionStateUtil.java │ └── configuration │ └── LocalUniResolverConfigurator.java ├── uni-resolver-web ├── .gitignore ├── src │ ├── test │ │ └── resources │ │ │ ├── jetty.xml │ │ │ ├── jetty-env.xml │ │ │ └── log4j2-test.xml │ └── main │ │ ├── resources │ │ ├── application-dev.yml │ │ └── log4j2.xml │ │ └── java │ │ └── uniresolver │ │ └── web │ │ ├── WebUniResolverApplication.java │ │ ├── config │ │ ├── ServletMappings.java │ │ ├── DriverConfigs.java │ │ └── WebAppConfig.java │ │ ├── WebUniResolverWebServerFactoryCustomizer.java │ │ ├── servlet │ │ ├── MethodsServlet.java │ │ ├── PropertiesServlet.java │ │ ├── TestIdentifiersServlet.java │ │ └── ServletUtil.java │ │ └── WebUniResolver.java ├── docker │ └── Dockerfile └── pom.xml ├── .gitignore ├── .github └── workflows │ ├── nightly-did-lint-check.yml │ ├── docker-release.yaml │ ├── universal-resolver-release.yaml │ ├── universal-resolver-build.yml │ ├── nightly-did-test-suite.yml │ ├── maven-snapshot.yml │ ├── maven-release.yml │ ├── universal-resolver-build-deploy.yml │ ├── archive_lint_reports.yml │ ├── kubernetes-deploy-to-cluster.yml │ └── docker-latest.yml ├── .gitlab-ci.yml └── .env /ci/run-did-test-suite/.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules -------------------------------------------------------------------------------- /examples/sovrin/lib/.gitignore: -------------------------------------------------------------------------------- 1 | libindy.so 2 | 3 | /.idea/ 4 | *.iml 5 | -------------------------------------------------------------------------------- /uni-resolver-core/openapi/java-client-generated/.openapi-generator-ignore: -------------------------------------------------------------------------------- 1 | .gitignore -------------------------------------------------------------------------------- /ci/get-driver-status/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | /app/config.json 3 | /app/driver-status-result-*.json 4 | -------------------------------------------------------------------------------- /docs/logo-dif.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hardcoding-1992/universal-resolver/HEAD/docs/logo-dif.png -------------------------------------------------------------------------------- /driver/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /examples/.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 -------------------------------------------------------------------------------- /docs/logo-ngi0pet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hardcoding-1992/universal-resolver/HEAD/docs/logo-ngi0pet.png -------------------------------------------------------------------------------- /driver-http/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /uni-resolver-client/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /uni-resolver-core/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /uni-resolver-local/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /uni-resolver-web/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | /.settings/ 4 | /target 5 | /bin/ 6 | /.idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/figures/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hardcoding-1992/universal-resolver/HEAD/docs/figures/overview.png -------------------------------------------------------------------------------- /docs/figures/protocol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hardcoding-1992/universal-resolver/HEAD/docs/figures/protocol.png -------------------------------------------------------------------------------- /docs/figures/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hardcoding-1992/universal-resolver/HEAD/docs/figures/architecture.png -------------------------------------------------------------------------------- /docs/figures/aws-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hardcoding-1992/universal-resolver/HEAD/docs/figures/aws-architecture.png -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/namespace/namespace-setup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: did 5 | labels: 6 | name: did 7 | -------------------------------------------------------------------------------- /driver/src/main/java/uniresolver/driver/AbstractDriver.java: -------------------------------------------------------------------------------- 1 | package uniresolver.driver; 2 | 3 | public abstract class AbstractDriver implements Driver { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/app-specs/secret-driver-did-sol.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: driver-did-sol 5 | type: Opaque 6 | data: 7 | PAYER: 8 | -------------------------------------------------------------------------------- /.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/ -------------------------------------------------------------------------------- /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: did 6 | data: 7 | backend_url: https://did.civic.com/ 8 | -------------------------------------------------------------------------------- /uni-resolver-web/src/test/resources/jetty.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /uni-resolver-web/src/test/resources/jetty-env.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /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=did \ 8 | --cluster="$CLUSTER_NAME" \ 9 | --user="$USER_NAME" 10 | -------------------------------------------------------------------------------- /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 ResolutionException, DereferencingException; 12 | } 13 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/app-specs/configmap-driver-did-sol.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: configmap-driver-did-sol 5 | data: 6 | config.json: | 7 | { 8 | "solanaRpcNodes": { 9 | "mainnet-beta": "https://twilight-small-flower.solana-mainnet.quiknode.pro/ec921b46257fea7637f4caed5780c80d49f4ab26", 10 | "devnet": "https://flashy-frosty-needle.solana-devnet.quiknode.pro/5b2ffc39b7ba4b592c3f0a8c92cd0df066a95838" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/w3c/DIDResolver.java: -------------------------------------------------------------------------------- 1 | package uniresolver.w3c; 2 | 3 | import uniresolver.ResolutionException; 4 | import uniresolver.result.ResolveDataModelResult; 5 | import uniresolver.result.ResolveRepresentationResult; 6 | 7 | import java.util.Map; 8 | 9 | public interface DIDResolver { 10 | 11 | public ResolveDataModelResult resolve(String didString, Map resolutionOptions) throws ResolutionException; 12 | public ResolveRepresentationResult resolveRepresentation(String didString, Map resolutionOptions) throws ResolutionException; 13 | } 14 | -------------------------------------------------------------------------------- /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 }} -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 ResolutionException, DereferencingException; 12 | 13 | default public DereferenceResult dereference(String didUrlString) throws ResolutionException, DereferencingException { 14 | return this.dereference(didUrlString, new HashMap<>()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/docker-release.yaml: -------------------------------------------------------------------------------- 1 | name: Docker release image 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | 8 | docker-release: 9 | uses: danubetech/workflows/.github/workflows/maven-triggered-docker-release.yml@main 10 | with: 11 | GLOBAL_IMAGE_NAME: universalresolver/uni-resolver-web 12 | GLOBAL_REPO_NAME: docker.io 13 | GLOBAL_FRAMEWORK: maven 14 | PATH_TO_DOCKERFILE: uni-resolver-web/docker/Dockerfile 15 | secrets: 16 | VAULT_ADDR: ${{ secrets.VAULT_ADDR }} 17 | CI_SECRET_READER_PERIODIC_TOKEN: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 18 | VAULTCA: ${{ secrets.VAULTCA }} 19 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 20 | -------------------------------------------------------------------------------- /.github/workflows/universal-resolver-release.yaml: -------------------------------------------------------------------------------- 1 | name: Build release version 2 | 3 | on: 4 | push: 5 | branches: 6 | - release-0.2.x 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@master 14 | - name: Docker Build and Push 15 | uses: danubetech/github-action-docker-build-push@master 16 | env: 17 | MAVEN_SETTINGS: ${{secrets.MAVEN_SETTINGS}} 18 | DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}} 19 | DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}} 20 | DOCKER_FILE: uni-resolver-web/docker/Dockerfile 21 | CONTAINER_TAG: identitydotcom/uni-resolver-web:0.2.0 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/log4j2-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/workflows/universal-resolver-build.yml: -------------------------------------------------------------------------------- 1 | name: Build latest tag 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - '.gitignore' 7 | - 'docs/**' 8 | - 'README.md' 9 | - 'LICENSE' 10 | branches: 11 | - main 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | environment: Dev 18 | steps: 19 | - uses: actions/checkout@master 20 | - name: Docker Build and Push 21 | uses: ./ci/docker-build-push 22 | env: 23 | DOCKER_FILE: uni-resolver-web/docker/Dockerfile 24 | CONTAINER_TAG: identitydotcom/uni-resolver-web:latest 25 | DOCKER_USERNAME : ${{ secrets.DOCKER_USERNAME }} 26 | DOCKER_PASSWORD : ${{ secrets.DOCKER_PASSWORD }} -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/deploy-k8s-aws/app-specs/deployment-uni-registrar-web.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: uni-registrar-web 5 | namespace: did 6 | labels: 7 | app: uni-registrar 8 | type: backend 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: uni-registrar-web 14 | template: 15 | metadata: 16 | labels: 17 | app: uni-registrar-web 18 | spec: 19 | containers: 20 | - name: uni-registrar-web 21 | image: universalregistrar/uni-registrar-web 22 | imagePullPolicy: Always 23 | ports: 24 | - containerPort: 9080 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: uni-registrar-web 30 | spec: 31 | type: NodePort 32 | selector: 33 | app: uni-registrar-web 34 | ports: 35 | - protocol: TCP 36 | port: 8080 37 | targetPort: 9080 38 | -------------------------------------------------------------------------------- /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.19-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 | -------------------------------------------------------------------------------- /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/deploy-k8s-aws/app-specs/deployment-uni-registrar-frontend.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: uni-registrar-frontend 5 | namespace: did 6 | labels: 7 | app: uni-registrar 8 | type: frontend 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: uni-registrar-frontend 14 | template: 15 | metadata: 16 | labels: 17 | app: uni-registrar-frontend 18 | spec: 19 | containers: 20 | - name: uni-registrar-frontend 21 | image: identitydotcom/uni-registrar-frontend 22 | imagePullPolicy: Always 23 | ports: 24 | - containerPort: 7082 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: uni-registrar-frontend 30 | spec: 31 | type: NodePort 32 | selector: 33 | app: uni-registrar-frontend 34 | ports: 35 | - protocol: TCP 36 | port: 8080 37 | targetPort: 7082 38 | -------------------------------------------------------------------------------- /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"] -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/result/StreamResult.java: -------------------------------------------------------------------------------- 1 | package uniresolver.result; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | 5 | public interface StreamResult extends Result { 6 | 7 | /* 8 | * Content stream methods 9 | */ 10 | 11 | @JsonIgnore 12 | public byte[] getFunctionContentStream(); 13 | 14 | @JsonIgnore 15 | public void setFunctionContentStream(byte[] functionContentStream); 16 | 17 | /* 18 | * Content type methods 19 | */ 20 | 21 | @JsonIgnore 22 | default public String getContentType() { 23 | return (String) this.getFunctionMetadata().get("contentType"); 24 | } 25 | 26 | @JsonIgnore 27 | default public void setContentType(String contentType) { 28 | if (contentType != null) 29 | this.getFunctionMetadata().put("contentType", contentType); 30 | else 31 | this.getFunctionMetadata().remove("contentType"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /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.btcr.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /uni-resolver-web/src/main/java/uniresolver/web/WebUniResolverApplication.java: -------------------------------------------------------------------------------- 1 | package uniresolver.web; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.builder.SpringApplicationBuilder; 6 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 7 | import org.springframework.context.annotation.Bean; 8 | import uniresolver.local.LocalUniResolver; 9 | 10 | @SpringBootApplication 11 | public class WebUniResolverApplication extends SpringBootServletInitializer { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(WebUniResolverApplication.class, args); 15 | } 16 | 17 | @Override 18 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 19 | return application.sources(WebUniResolverApplication.class); 20 | } 21 | 22 | @Bean(name = "UniResolver") 23 | public LocalUniResolver localUniResolver() { 24 | return new LocalUniResolver(); 25 | } 26 | } -------------------------------------------------------------------------------- /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 | MAINTAINER Markus Sabadello 20 | 21 | WORKDIR /opt/universal-resolver/uni-resolver-web/ 22 | 23 | COPY --from=build /opt/universal-resolver/uni-resolver-web/target/*-exec.jar ./ 24 | 25 | ENV uniresolver_web_spring_profiles_active=default 26 | 27 | # done 28 | 29 | EXPOSE 8080 30 | CMD java -jar *.jar 31 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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.19-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 | com.google.code.gson 29 | gson 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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: did 6 | labels: 7 | app: uni-resolver 8 | type: frontend 9 | spec: 10 | replicas: 3 11 | selector: 12 | matchLabels: 13 | app: uni-resolver-frontend 14 | template: 15 | metadata: 16 | labels: 17 | app: uni-resolver-frontend 18 | fargate: "true" 19 | spec: 20 | containers: 21 | - name: uni-resolver-frontend 22 | image: universalresolver/universal-resolver-frontend 23 | imagePullPolicy: Always 24 | ports: 25 | - containerPort: 7081 26 | env: 27 | - name: BACKEND_URL 28 | valueFrom: 29 | configMapKeyRef: 30 | name: uni-resolver-frontend 31 | key: backend_url 32 | --- 33 | apiVersion: v1 34 | kind: Service 35 | metadata: 36 | name: uni-resolver-frontend 37 | namespace: did 38 | spec: 39 | type: NodePort 40 | selector: 41 | app: uni-resolver-frontend 42 | ports: 43 | - protocol: TCP 44 | port: 7081 45 | targetPort: 7081 46 | -------------------------------------------------------------------------------- /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, boolean resolveRepresentation, 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-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 | 15 | public String getProperties() { 16 | return properties; 17 | } 18 | 19 | public String getResolve() { 20 | return resolve; 21 | } 22 | 23 | public String getMethods() { 24 | return methods; 25 | } 26 | 27 | public String getTestIdentifiers() { 28 | return testIdentifiers; 29 | } 30 | 31 | public void setProperties(String properties) { 32 | this.properties = properties; 33 | } 34 | 35 | public void setResolve(String resolve) { 36 | this.resolve = resolve; 37 | } 38 | 39 | public void setMethods(String methods) { 40 | this.methods = methods; 41 | } 42 | 43 | public void setTestIdentifiers(String testIdentifiers) { 44 | this.testIdentifiers = testIdentifiers; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | mkdir -p /tmp/kube 11 | touch /tmp/kube/kubeconfig 12 | export KUBECONFIG=/tmp/kube/kubeconfig 13 | chmod 600 $KUBECONFIG 14 | 15 | if [ "$ENVIRONMENT" = Prod ]; then export CLUSTER="civic-prod"; else export CLUSTER="civic-dev"; fi 16 | 17 | echo "ENVIRONMENT: $ENVIRONMENT" 18 | echo "CLUSTER: $CLUSTER" 19 | 20 | aws eks --region us-east-1 update-kubeconfig --name $CLUSTER 21 | 22 | echo "## Kubeconfig ##" 23 | kubectl config view 24 | 25 | cp /*.py /*.yaml -r /app-specs -r /namespace . 2>/dev/null || : 26 | 27 | echo "Current workspace Folder" 28 | pwd 29 | ls -al . 30 | python --version 31 | python prepare-deployment.py 32 | 33 | echo "Driver config script" 34 | python driver-config.py 35 | 36 | # re-enable if the BTCR driver is enabled 37 | #echo "Apply did-btcr secrets" 38 | #python substitute-btcr-driver-values.py --url "$RPC_URL_TESTNET" --cert "$RPC_CERT_TESTNET" 39 | # 40 | echo "## Deployment Folder ##" 41 | cd deploy 42 | #cat deployment-driver-did-btcr.yaml 43 | ls -al . 44 | 45 | echo "### Deploying following Specs ### " 46 | cat deploy.sh 47 | 48 | 49 | ./deploy.sh 50 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; 9 | import org.springframework.boot.web.server.WebServerFactoryCustomizer; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | public class WebUniResolverWebServerFactoryCustomizer implements WebServerFactoryCustomizer { 14 | 15 | @Override 16 | public void customize(JettyServletWebServerFactory factory) { 17 | factory.addServerCustomizers(this::customizeUriCompliance); 18 | } 19 | 20 | private void customizeUriCompliance(Server server) { 21 | for (Connector connector : server.getConnectors()) { 22 | connector.getConnectionFactories().stream() 23 | .filter(factory -> factory instanceof HttpConnectionFactory) 24 | .forEach(factory -> { 25 | HttpConfiguration httpConfig = ((HttpConnectionFactory) factory).getHttpConfiguration(); 26 | httpConfig.setUriCompliance(UriCompliance.UNSAFE); 27 | }); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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.19-SNAPSHOT 12 | 13 | 14 | 15 | 1.16.1 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 | -------------------------------------------------------------------------------- /.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/maven-snapshot.yml: -------------------------------------------------------------------------------- 1 | name: Maven snapshot artifact 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '.gitignore' 7 | - 'docs/**' 8 | - 'README.md' 9 | - 'LICENSE' 10 | branches: [main] 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@master 18 | - name: Import Secrets 19 | uses: hashicorp/vault-action@v2.3.0 20 | with: 21 | url: ${{ secrets.VAULT_ADDR }} 22 | token: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 23 | caCertificate: ${{ secrets.VAULTCA }} 24 | secrets: | 25 | ci/data/gh-workflows/maven-danubetech-nexus username | MAVEN_USERNAME ; 26 | ci/data/gh-workflows/maven-danubetech-nexus password | MAVEN_PASSWORD ; 27 | ci/data/gh-workflows/deployment-status slack-webhook-url | SLACK_WEBHOOK_URL 28 | - name: Run maven deploy action 29 | uses: danubetech/github-action-maven-deploy@master 30 | with: 31 | server_id: danubetech-maven-snapshots 32 | path_to_pom: /github/workspace/ 33 | env: 34 | MAVEN_USERNAME: ${{ env.MAVEN_USERNAME }} 35 | MAVEN_PASSWORD: ${{ env.MAVEN_PASSWORD }} 36 | - name: Slack notification 37 | uses: 8398a7/action-slack@v3 38 | with: 39 | status: ${{ job.status }} 40 | fields: repo,commit,action,eventName,ref,workflow 41 | env: 42 | SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }} 43 | if: failure() 44 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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: did 6 | labels: 7 | app: uni-resolver 8 | type: backend 9 | spec: 10 | replicas: 3 11 | selector: 12 | matchLabels: 13 | app: uni-resolver-web 14 | template: 15 | metadata: 16 | labels: 17 | app: uni-resolver-web 18 | fargate: "true" 19 | spec: 20 | containers: 21 | - name: uni-resolver-web 22 | # Use the identity.com Docker image for the Universal Resolver Web 23 | # so that we can control the did methods supported - specifically add preview methods like "did:icp" 24 | # not yet supported in the upstream. 25 | # If we do not have any unofficial ones, we can and should use the upstream image instead 26 | image: identitydotcom/uni-resolver-web 27 | imagePullPolicy: Always 28 | ports: 29 | - containerPort: 8080 30 | livenessProbe: 31 | httpGet: 32 | path: "/1.0/methods" 33 | port: 8080 34 | initialDelaySeconds: 30 35 | periodSeconds: 10 36 | timeoutSeconds: 5 37 | successThreshold: 1 38 | failureThreshold: 10 # set high to avoid unnecessary pod restarts 39 | --- 40 | apiVersion: v1 41 | kind: Service 42 | metadata: 43 | name: uni-resolver-web 44 | namespace: did 45 | spec: 46 | type: NodePort 47 | selector: 48 | app: uni-resolver-web 49 | ports: 50 | - protocol: TCP 51 | port: 8080 52 | targetPort: 8080 53 | -------------------------------------------------------------------------------- /ci/deploy-k8s-aws/scripts/k8s-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{containerName}} 5 | namespace: did 6 | labels: 7 | app: {{containerName}} 8 | spec: 9 | replicas: 3 10 | selector: 11 | matchLabels: 12 | app: {{containerName}} 13 | template: 14 | metadata: 15 | labels: 16 | app: {{containerName}} 17 | fargate: "true" 18 | spec: 19 | containers: 20 | - name: {{containerName}} 21 | image: {{containerTag}} 22 | imagePullPolicy: Always 23 | resources: 24 | # Since Amazon EKS Fargate runs only one pod per node, 25 | # all Amazon EKS Fargate pods run with guaranteed priority, 26 | # so the requested CPU and memory must be equal to the limit. 27 | # Reference: https://docs.aws.amazon.com/eks/latest/userguide/fargate-pod-configuration.html 28 | requests: 29 | memory: "512Mi" 30 | cpu: "500m" 31 | # memory: "128Mi" 32 | # cpu: "5m" 33 | limits: 34 | memory: "512Mi" 35 | cpu: "500m" 36 | ports: 37 | - containerPort: {{containerPort}} 38 | securityContext: 39 | capabilities: 40 | drop: 41 | - NET_RAW 42 | {{environmentVariables}} 43 | {{configMapVolume}} 44 | 45 | --- 46 | apiVersion: v1 47 | kind: Service 48 | metadata: 49 | name: {{containerName}} 50 | namespace: did 51 | spec: 52 | type: NodePort 53 | selector: 54 | app: {{containerName}} 55 | ports: 56 | - protocol: TCP 57 | port: {{containerPort}} 58 | targetPort: {{containerPort}} 59 | -------------------------------------------------------------------------------- /.github/workflows/maven-release.yml: -------------------------------------------------------------------------------- 1 | name: Maven release artifact 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | 8 | maven-release: 9 | uses: danubetech/workflows/.github/workflows/maven-release.yml@main 10 | with: 11 | GLOBAL_MAVEN_REPO_SERVER_ID: danubetech-maven-releases 12 | secrets: 13 | VAULT_ADDR: ${{ secrets.VAULT_ADDR }} 14 | CI_SECRET_READER_PERIODIC_TOKEN: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 15 | VAULTCA: ${{ secrets.VAULTCA }} 16 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 17 | 18 | trigger-related-workflows: 19 | needs: [ maven-release ] 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Import Secrets 23 | uses: hashicorp/vault-action@v2.3.1 24 | with: 25 | url: ${{ secrets.VAULT_ADDR }} 26 | token: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 27 | caCertificate: ${{ secrets.VAULTCA }} 28 | secrets: | 29 | ci/data/gh-workflows/workflow-dispatch token | WORKFLOW_DISPATCH_TOKEN ; 30 | ci/data/gh-workflows/deployment-status slack-webhook-url | SLACK_WEBHOOK_URL 31 | 32 | - name: Repository Dispatch 33 | uses: benc-uk/workflow-dispatch@v1 34 | with: 35 | token: ${{ env.WORKFLOW_DISPATCH_TOKEN }} 36 | workflow: "Docker release image" # docker-release.yml 37 | 38 | - name: Slack notification 39 | if: failure() 40 | uses: 8398a7/action-slack@v3 41 | with: 42 | status: ${{ job.status }} 43 | fields: repo,commit,action,eventName,ref,workflow 44 | env: 45 | SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }} 46 | -------------------------------------------------------------------------------- /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.19-SNAPSHOT 12 | 13 | 14 | 15 | 6.0.0 16 | 6.1.6 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 | -------------------------------------------------------------------------------- /.github/workflows/universal-resolver-build-deploy.yml: -------------------------------------------------------------------------------- 1 | name: AWS Kubernetes deployment 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '.gitignore' 7 | - 'docs/**' 8 | - 'README.md' 9 | - 'LICENSE' 10 | branches: [main, 'test-driver-**', gh-workflows] 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | environment: Dev 17 | steps: 18 | - uses: actions/checkout@master 19 | # - name: Docker Build and Push 20 | # uses: ./ci/docker-build-push 21 | # env: 22 | # DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}} 23 | # DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}} 24 | # DOCKER_FILE: uni-resolver-web/docker/Dockerfile 25 | # CONTAINER_TAG: universalresolver/uni-resolver-web:latest 26 | - name: Deploy to AWS 27 | uses: ./ci/deploy-k8s-aws 28 | env: 29 | KUBE_CONFIG_DATA: ${{secrets.KUBE_CONFIG_DATA}} 30 | AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}} 31 | AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}} 32 | RPC_URL_TESTNET: ${{secrets.RPC_URL_TESTNET}} 33 | RPC_CERT_TESTNET: ${{secrets.RPC_CERT_TESTNET}} 34 | - name: Sleep for 120 seconds 35 | uses: jakejarvis/wait-action@master 36 | with: 37 | time: '120s' 38 | - name: Smoke Test 39 | uses: ./ci/smoke-tests 40 | with: 41 | host: https://dev.uniresolver.io 42 | - name: Slack notification 43 | uses: 8398a7/action-slack@v3 44 | with: 45 | status: ${{ job.status }} 46 | fields: repo,commit,action,eventName,ref,workflow # selectable (default: repo,message) 47 | env: 48 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required 49 | if: failure() # Send message only on failure 50 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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-sol: 53 | envFrom: 54 | - secretRef: 55 | name: driver-did-sol -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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.StringJoiner; 8 | 9 | @Configuration 10 | @ConfigurationProperties("uniresolver") 11 | public class DriverConfigs { 12 | 13 | private List drivers; 14 | 15 | public List getDrivers() { 16 | return drivers; 17 | } 18 | 19 | public void setDrivers(List drivers) { 20 | this.drivers = drivers; 21 | } 22 | 23 | public static class DriverConfig { 24 | 25 | private String pattern; 26 | private String url; 27 | private String propertiesEndpoint; 28 | private List testIdentifiers; 29 | 30 | public String getPattern() { 31 | return pattern; 32 | } 33 | 34 | public void setPattern(String value) { 35 | this.pattern = value; 36 | } 37 | 38 | public String getUrl() { 39 | return url; 40 | } 41 | 42 | public void setUrl(String value) { 43 | this.url = value; 44 | } 45 | 46 | public String getPropertiesEndpoint() { 47 | return propertiesEndpoint; 48 | } 49 | 50 | public void setPropertiesEndpoint(String value) { 51 | this.propertiesEndpoint = value; 52 | } 53 | 54 | public List getTestIdentifiers() { 55 | return testIdentifiers; 56 | } 57 | 58 | public void setTestIdentifiers(List value) { 59 | this.testIdentifiers = value; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return new StringJoiner(", ", DriverConfig.class.getSimpleName() + "[", "]").add( 65 | "pattern='" + pattern + "'") 66 | .add("url='" + url + "'") 67 | .add("propertiesEndpoint='" + propertiesEndpoint + "'") 68 | .add("testIdentifiers=" + testIdentifiers) 69 | .toString(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /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_MIME_TYPE, methodsString); 59 | } 60 | } -------------------------------------------------------------------------------- /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, boolean resolveRepresentation, 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, boolean resolveRepresentation, 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-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_MIME_TYPE, propertiesString); 59 | } 60 | } -------------------------------------------------------------------------------- /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_MIME_TYPE, testIdentifiersString); 60 | } 61 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/src/main/java/uniresolver/examples/w3ctestsuite/TestIdentifier.java: -------------------------------------------------------------------------------- 1 | package uniresolver.examples.w3ctestsuite; 2 | 3 | import foundation.identity.did.DID; 4 | import foundation.identity.did.DIDURL; 5 | import foundation.identity.did.parameters.Parameters; 6 | import uniresolver.ResolutionException; 7 | import uniresolver.client.ClientUniResolver; 8 | import uniresolver.result.ResolveResult; 9 | 10 | import java.net.URLEncoder; 11 | import java.nio.charset.StandardCharsets; 12 | import java.util.ArrayList; 13 | import java.util.LinkedHashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | public class TestIdentifier { 18 | 19 | static final List 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 23 | super(); 24 | } 25 | 26 | @Override 27 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 28 | 29 | // read request 30 | 31 | request.setCharacterEncoding("UTF-8"); 32 | response.setCharacterEncoding("UTF-8"); 33 | 34 | if (log.isInfoEnabled()) log.info("Incoming request."); 35 | 36 | // get properties 37 | 38 | Map properties; 39 | String propertiesString; 40 | 41 | try { 42 | 43 | properties = InitServlet.getDriver() == null ? null : InitServlet.getDriver().properties(); 44 | propertiesString = properties == null ? null : objectMapper.writeValueAsString(properties); 45 | } catch (Exception ex) { 46 | 47 | if (log.isWarnEnabled()) log.warn("Properties problem: " + ex.getMessage(), ex); 48 | ServletUtil.sendResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Properties problem: " + ex.getMessage()); 49 | return; 50 | } 51 | 52 | if (log.isInfoEnabled()) log.info("Properties: " + properties); 53 | 54 | // no properties? 55 | 56 | if (properties == null) { 57 | 58 | ServletUtil.sendResponse(response, HttpServletResponse.SC_NOT_FOUND, "No properties."); 59 | return; 60 | } 61 | 62 | // write properties 63 | 64 | ServletUtil.sendResponse(response, HttpServletResponse.SC_OK, Driver.PROPERTIES_MIME_TYPE, propertiesString); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 { 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 | } -------------------------------------------------------------------------------- /driver/src/main/java/uniresolver/driver/Driver.java: -------------------------------------------------------------------------------- 1 | package uniresolver.driver; 2 | 3 | import foundation.identity.did.DID; 4 | import foundation.identity.did.representations.Representations; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import uniresolver.ResolutionException; 8 | import uniresolver.result.ResolveDataModelResult; 9 | import uniresolver.result.ResolveRepresentationResult; 10 | 11 | import java.util.Collections; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | public interface Driver { 17 | 18 | public static final String PROPERTIES_MIME_TYPE = "application/json"; 19 | 20 | static final Logger log = LoggerFactory.getLogger(Driver.class); 21 | 22 | default public ResolveDataModelResult resolve(DID did, Map resolutionOptions) throws ResolutionException { 23 | if (log.isDebugEnabled()) log.debug("Driver: resolve(" + did + ") with options: " + resolutionOptions); 24 | 25 | String accept = (String) resolutionOptions.get("accept"); 26 | if (accept != null) throw new ResolutionException("Driver: Unexpected 'accept' provided in 'resolutionOptions' for resolve()."); 27 | 28 | Map resolveRepresentationResolutionOptions = Map.of("accept", Representations.DEFAULT_MEDIA_TYPE); 29 | ResolveRepresentationResult resolveRepresentationResult = this.resolveRepresentation(did, resolveRepresentationResolutionOptions); 30 | 31 | return resolveRepresentationResult == null ? null : resolveRepresentationResult.toResolveDataModelResult(); 32 | } 33 | 34 | default public ResolveRepresentationResult resolveRepresentation(DID did, Map resolutionOptions) throws ResolutionException { 35 | if (log.isDebugEnabled()) log.debug("Driver: resolveRepresentation(" + did + ") with options: " + resolutionOptions); 36 | 37 | String accept = (String) resolutionOptions.get("accept"); 38 | 39 | Map resolveDataModelResolutionOptions = new HashMap<>(resolutionOptions); 40 | resolveDataModelResolutionOptions.remove("accept"); 41 | ResolveDataModelResult resolveDataModelResult = this.resolve(did, resolveDataModelResolutionOptions); 42 | 43 | return resolveDataModelResult == null ? null : resolveDataModelResult.toResolveRepresentationResult(accept); 44 | } 45 | 46 | default public Map properties() throws ResolutionException { 47 | return Collections.emptyMap(); 48 | } 49 | 50 | default public List testIdentifiers() throws ResolutionException { 51 | return Collections.emptyList(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /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 triggerd 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/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 | -------------------------------------------------------------------------------- /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 org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import uniresolver.DereferencingException; 8 | import uniresolver.ResolutionException; 9 | import uniresolver.local.LocalUniDereferencer; 10 | import uniresolver.local.extensions.DereferencerExtension; 11 | import uniresolver.local.extensions.ExtensionStatus; 12 | import uniresolver.result.DereferenceResult; 13 | import uniresolver.result.ResolveRepresentationResult; 14 | import uniresolver.result.ResolveResult; 15 | 16 | import java.util.Map; 17 | 18 | public class DIDDocumentExtension implements DereferencerExtension.DereferencePrimaryDereferencerExtension { 19 | 20 | private static final Logger log = LoggerFactory.getLogger(DIDDocumentExtension.class); 21 | 22 | @Override 23 | public ExtensionStatus dereferencePrimary(DIDURL didUrlWithoutFragment, Map dereferenceOptions, ResolveResult resolveResult, DereferenceResult dereferenceResult, Map executionState, LocalUniDereferencer localUniDereferencer) throws ResolutionException, DereferencingException { 24 | 25 | // check inputs 26 | 27 | String accept = (String) dereferenceOptions.get("accept"); 28 | 29 | if (didUrlWithoutFragment.getPath() != null) { 30 | if (log.isDebugEnabled()) log.debug("Skipping (DID URL has path)."); 31 | return null; 32 | } 33 | 34 | if (resolveResult == null) { 35 | if (log.isDebugEnabled()) log.debug("Skipping (no resolve result)."); 36 | return null; 37 | } 38 | 39 | if (log.isInfoEnabled()) log.info("Executing dereferencePrimary() with extension " + this.getClass().getName()); 40 | 41 | // dereference 42 | 43 | if (! Representations.isRepresentationMediaType(accept)) { 44 | throw new DereferencingException(DereferencingException.ERROR_CONTENTTYEPNOTSUPPORTED, "Content type not supported: " + accept); 45 | } 46 | 47 | ResolveRepresentationResult resolveRepresentationResult = resolveResult.toResolveRepresentationResult(accept); 48 | 49 | if (log.isDebugEnabled()) log.debug("Dereferencing DID URL that has no path (assuming DID document): " + didUrlWithoutFragment); 50 | 51 | // update result 52 | 53 | dereferenceResult.setContentType(resolveRepresentationResult.getContentType()); 54 | dereferenceResult.setContentStream(resolveRepresentationResult.getDidDocumentStream()); 55 | dereferenceResult.setContentMetadata(resolveRepresentationResult.getDidDocumentMetadata()); 56 | 57 | // done 58 | 59 | return ExtensionStatus.SKIP_DEREFERENCE_PRIMARY; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-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.ResolveDataModelResult; 7 | import uniresolver.result.ResolveRepresentationResult; 8 | import uniresolver.w3c.DIDResolver; 9 | 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | 15 | public interface UniResolver extends DIDResolver { 16 | 17 | public static final String PROPERTIES_MIME_TYPE = "application/json"; 18 | public static final String METHODS_MIME_TYPE = "application/json"; 19 | public static final String TEST_IDENTIFIER_MIME_TYPE = "application/json"; 20 | 21 | static final Logger log = LoggerFactory.getLogger(UniResolver.class); 22 | 23 | @Override 24 | default public ResolveDataModelResult resolve(String didString, Map resolutionOptions) throws ResolutionException { 25 | if (log.isDebugEnabled()) log.debug("resolve(" + didString + ") with options: " + resolutionOptions); 26 | 27 | if (didString == null) throw new NullPointerException(); 28 | if (resolutionOptions == null) resolutionOptions = new HashMap<>(); 29 | 30 | String accept = (String) resolutionOptions.get("accept"); 31 | if (accept != null) throw new ResolutionException("Unexpected 'accept' provided in 'resolutionOptions' for resolve()."); 32 | 33 | Map resolveRepresentationResolutionOptions = Map.of("accept", Representations.DEFAULT_MEDIA_TYPE); 34 | 35 | ResolveRepresentationResult resolveRepresentationResult = this.resolveRepresentation(didString, resolveRepresentationResolutionOptions); 36 | 37 | return resolveRepresentationResult == null ? null : resolveRepresentationResult.toResolveDataModelResult(); 38 | } 39 | 40 | @Override 41 | default public ResolveRepresentationResult resolveRepresentation(String didString, Map resolutionOptions) throws ResolutionException { 42 | if (log.isDebugEnabled()) log.debug("resolveRepresentation(" + didString + ") with options: " + resolutionOptions); 43 | 44 | if (didString == null) throw new NullPointerException(); 45 | if (resolutionOptions == null) resolutionOptions = new HashMap<>(); 46 | 47 | String accept = (String) resolutionOptions.get("accept"); 48 | 49 | Map resolveDataModelResolutionOptions = new HashMap<>(resolutionOptions); 50 | resolveDataModelResolutionOptions.remove("accept"); 51 | 52 | ResolveDataModelResult resolveDataModelResult = this.resolve(didString, resolveDataModelResolutionOptions); 53 | 54 | return resolveDataModelResult == null ? null : resolveDataModelResult.toResolveRepresentationResult(accept); 55 | } 56 | 57 | default public ResolveDataModelResult resolve(String didString) throws ResolutionException { 58 | return this.resolve(didString, new HashMap<>()); 59 | } 60 | 61 | default public ResolveRepresentationResult resolveRepresentation(String didString) throws ResolutionException { 62 | return this.resolveRepresentation(didString, new HashMap<>()); 63 | } 64 | 65 | public Map> properties() throws ResolutionException; 66 | public Set methods() throws ResolutionException; 67 | public Map> testIdentifiers() throws ResolutionException; 68 | } 69 | -------------------------------------------------------------------------------- /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.util.ArrayList; 6 | import java.util.LinkedHashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public interface Result { 11 | 12 | /* 13 | * Serialization 14 | */ 15 | 16 | public Map toMap(); 17 | public String toJson(); 18 | 19 | /* 20 | * Metadata methods 21 | */ 22 | 23 | @JsonIgnore 24 | public Map getFunctionMetadata(); 25 | 26 | @JsonIgnore 27 | public Map getFunctionContentMetadata(); 28 | 29 | /* 30 | * Error methods 31 | */ 32 | 33 | @JsonIgnore 34 | default public boolean isErrorResult() { 35 | return this.getError() != null; 36 | } 37 | 38 | @JsonIgnore 39 | default public String getError() { 40 | if (this.getFunctionMetadata() == null) throw new NullPointerException(); 41 | return (String) this.getFunctionMetadata().get("error"); 42 | } 43 | 44 | @JsonIgnore 45 | default public void setError(String error) { 46 | if (this.getFunctionMetadata() == null) throw new NullPointerException(); 47 | if (error != null) 48 | this.getFunctionMetadata().put("error", error); 49 | else 50 | this.getFunctionMetadata().remove("error"); 51 | } 52 | 53 | @JsonIgnore 54 | default public String getErrorMessage() { 55 | if (this.getFunctionMetadata() == null) throw new NullPointerException(); 56 | return (String) this.getFunctionMetadata().get("errorMessage"); 57 | } 58 | 59 | @JsonIgnore 60 | default public void setErrorMessage(String error) { 61 | if (this.getFunctionMetadata() == null) throw new NullPointerException(); 62 | if (error != null) 63 | this.getFunctionMetadata().put("errorMessage", error); 64 | else 65 | this.getFunctionMetadata().remove("errorMessage"); 66 | } 67 | 68 | /* 69 | * Problem/warning methods 70 | */ 71 | 72 | @JsonIgnore 73 | default public boolean hasWarnings() { 74 | return this.getWarnings() != null && !this.getWarnings().isEmpty(); 75 | } 76 | 77 | @JsonIgnore 78 | default public List> getWarnings() { 79 | return this.getFunctionMetadata() == null ? null : (List>) this.getFunctionMetadata().get("warnings"); 80 | } 81 | 82 | @JsonIgnore 83 | default public void addWarning(String message, Map warningMetadata) { 84 | if (message == null) throw new NullPointerException(); 85 | if (this.getFunctionMetadata() == null) throw new NullPointerException(); 86 | List> warnings = (List>) this.getFunctionMetadata().get("warnings"); 87 | if (warnings == null) { warnings = new ArrayList<>(); this.getFunctionMetadata().put("warnings", warnings); } 88 | Map warning = new LinkedHashMap<>(); 89 | if (message != null) warning.put("message", message); 90 | if (warningMetadata != null) warning.putAll(warningMetadata); 91 | warnings.add(warning); 92 | } 93 | 94 | @JsonIgnore 95 | default public void addWarning(String message) { 96 | this.addWarning(message, null); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /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.ResolutionException; 13 | import uniresolver.UniResolver; 14 | import uniresolver.result.ResolveDataModelResult; 15 | import uniresolver.result.ResolveRepresentationResult; 16 | 17 | import java.io.IOException; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.Set; 21 | @WebServlet 22 | public abstract class WebUniResolver extends HttpServlet implements HttpRequestHandler, UniResolver { 23 | 24 | @Autowired 25 | @Qualifier("UniResolver") 26 | private UniResolver uniResolver; 27 | 28 | protected WebUniResolver() { 29 | 30 | super(); 31 | } 32 | 33 | @Override 34 | public void handleRequest(HttpServletRequest request, @NonNull HttpServletResponse response) throws ServletException, IOException { 35 | if ("GET".equals(request.getMethod())) this.doGet(request, response); 36 | if ("POST".equals(request.getMethod())) this.doPost(request, response); 37 | if ("PUT".equals(request.getMethod())) this.doPut(request, response); 38 | if ("DELETE".equals(request.getMethod())) this.doDelete(request, response); 39 | if ("OPTIONS".equals(request.getMethod())) this.doOptions(request, response); 40 | } 41 | 42 | @Override 43 | protected void doOptions(HttpServletRequest request, HttpServletResponse response) { 44 | response.setHeader("Access-Control-Allow-Origin", "*"); 45 | response.setHeader("Access-Control-Allow-Headers", "Accept, Content-Type"); 46 | response.setStatus(HttpServletResponse.SC_OK); 47 | } 48 | 49 | @Override 50 | public ResolveDataModelResult resolve(String didString, Map resolutionOptions) throws ResolutionException { 51 | return this.getUniResolver() == null ? null : this.getUniResolver().resolve(didString, resolutionOptions); 52 | } 53 | 54 | @Override 55 | public ResolveRepresentationResult resolveRepresentation(String didString, Map resolutionOptions) throws ResolutionException { 56 | return this.getUniResolver() == null ? null : this.getUniResolver().resolveRepresentation(didString, resolutionOptions); 57 | } 58 | 59 | @Override 60 | public Map> properties() throws ResolutionException { 61 | return this.getUniResolver() == null ? null : this.getUniResolver().properties(); 62 | } 63 | 64 | @Override 65 | public Set methods() throws ResolutionException { 66 | return this.getUniResolver() == null ? null : this.getUniResolver().methods(); 67 | } 68 | 69 | @Override 70 | public Map> testIdentifiers() throws ResolutionException { 71 | return this.getUniResolver() == null ? null : this.getUniResolver().testIdentifiers(); 72 | } 73 | 74 | /* 75 | * Getters and setters 76 | */ 77 | 78 | public UniResolver getUniResolver() { 79 | return this.uniResolver; 80 | } 81 | 82 | public void setUniResolver(UniResolver uniResolver) { 83 | this.uniResolver = uniResolver; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /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 follwing prefixes for branches: 14 | 15 | - feature-\ -> adding/changeing 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 | -------------------------------------------------------------------------------- /uni-resolver-local/src/main/java/uniresolver/local/configuration/LocalUniResolverConfigurator.java: -------------------------------------------------------------------------------- 1 | package uniresolver.local.configuration; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import uniresolver.driver.Driver; 10 | import uniresolver.driver.http.HttpDriver; 11 | import uniresolver.local.LocalUniResolver; 12 | 13 | import java.io.FileReader; 14 | import java.io.IOException; 15 | import java.io.Reader; 16 | import java.net.URI; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | public class LocalUniResolverConfigurator { 21 | 22 | private static final Logger log = LoggerFactory.getLogger(LocalUniResolverConfigurator.class); 23 | 24 | public static void configureLocalUniResolver(String filePath, LocalUniResolver localUniResolver) throws IOException { 25 | 26 | final Gson gson = new Gson(); 27 | 28 | List drivers = new ArrayList<>(); 29 | 30 | try (Reader reader = new FileReader(filePath)) { 31 | 32 | JsonObject jsonObjectRoot = gson.fromJson(reader, JsonObject.class); 33 | JsonArray jsonArrayDrivers = jsonObjectRoot.getAsJsonArray("drivers"); 34 | 35 | for (JsonElement jsonArrayDriver : jsonArrayDrivers) { 36 | 37 | JsonObject jsonObjectDriver = (JsonObject) jsonArrayDriver; 38 | 39 | String pattern = jsonObjectDriver.has("pattern") ? jsonObjectDriver.get("pattern").getAsString() : null; 40 | String url = jsonObjectDriver.has("url") ? jsonObjectDriver.get("url").getAsString() : null; 41 | String propertiesEndpoint = jsonObjectDriver.has("propertiesEndpoint") ? jsonObjectDriver.get("propertiesEndpoint").getAsString() : null; 42 | JsonArray testIdentifiers = jsonObjectDriver.has("testIdentifiers") ? jsonObjectDriver.get("testIdentifiers").getAsJsonArray() : 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 | 51 | driver.setPattern(pattern); 52 | 53 | if (url.contains("$1") || url.contains("$2")) { 54 | 55 | driver.setResolveUri(url); 56 | driver.setPropertiesUri((URI) null); 57 | } else { 58 | 59 | if (! url.endsWith("/")) url = url + "/"; 60 | 61 | driver.setResolveUri(url + "1.0/identifiers/"); 62 | if ("true".equals(propertiesEndpoint)) driver.setPropertiesUri(url + "1.0/properties"); 63 | } 64 | 65 | if (testIdentifiers != null) { 66 | driver.setTestIdentifiers(testIdentifiers.asList().stream().map(JsonElement::getAsString).toList()); 67 | } 68 | 69 | // done 70 | 71 | drivers.add(driver); 72 | if (log.isInfoEnabled()) log.info("Added driver for pattern '" + pattern + "' at " + driver.getResolveUri() + " (" + driver.getPropertiesUri() + ")"); 73 | } 74 | } 75 | 76 | // done 77 | 78 | localUniResolver.setDrivers(drivers); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /.github/workflows/docker-latest.yml: -------------------------------------------------------------------------------- 1 | name: 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 | env: 14 | IMAGE_NAME: universalresolver/uni-resolver-web 15 | PATH_TO_DOCKERFILE: $GITHUB_WORKSPACE/uni-resolver-web/docker/Dockerfile 16 | BUILD_CONTEXT: $GITHUB_WORKSPACE 17 | 18 | jobs: 19 | publish-image: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@master 23 | - name: Import Secrets 24 | uses: hashicorp/vault-action@v2.3.0 25 | with: 26 | url: ${{ secrets.VAULT_ADDR }} 27 | token: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 28 | caCertificate: ${{ secrets.VAULTCA }} 29 | secrets: | 30 | ci/data/gh-workflows/dockerhub username | DOCKER_USERNAME ; 31 | ci/data/gh-workflows/dockerhub password | DOCKER_PASSWORD ; 32 | ci/data/gh-workflows/maven-danubetech-nexus username | MAVEN_USERNAME ; 33 | ci/data/gh-workflows/maven-danubetech-nexus password | MAVEN_PASSWORD ; 34 | ci/data/gh-workflows/deployment-status slack-webhook-url | SLACK_WEBHOOK_URL 35 | - name: Build image 36 | run: | 37 | docker build "${{ env.BUILD_CONTEXT }}" -f "${{ env.PATH_TO_DOCKERFILE }}" -t "${{ env.IMAGE_NAME }}" \ 38 | --build-arg DANUBETECH_MAVEN_INTERNAL_USERNAME=${{ env.MAVEN_USERNAME }} \ 39 | --build-arg DANUBETECH_MAVEN_INTERNAL_PASSWORD=${{ env.MAVEN_PASSWORD }} 40 | - name: Login user to Dockerhub 41 | run: echo "${{ env.DOCKER_PASSWORD }}" | docker login -u "${{ env.DOCKER_USERNAME }}" --password-stdin 42 | - name: Push image to Dockerhub 43 | run: docker push "${{ env.IMAGE_NAME }}" 44 | - name: Slack notification 45 | uses: 8398a7/action-slack@v3 46 | with: 47 | status: ${{ job.status }} 48 | fields: repo,commit,action,eventName,ref,workflow 49 | env: 50 | SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }} 51 | if: failure() 52 | trigger-related-workflows: 53 | needs: [publish-image] 54 | runs-on: ubuntu-latest 55 | steps: 56 | - name: Import Secrets 57 | uses: hashicorp/vault-action@v2.3.0 58 | with: 59 | url: ${{ secrets.VAULT_ADDR }} 60 | token: ${{ secrets.CI_SECRET_READER_PERIODIC_TOKEN }} 61 | caCertificate: ${{ secrets.VAULTCA }} 62 | secrets: | 63 | ci/data/gh-workflows/workflow-dispatch token | WORKFLOW_DISPATCH_TOKEN ; 64 | ci/data/gh-workflows/deployment-status slack-webhook-url | SLACK_WEBHOOK_URL 65 | - name: Dispatch to AWS Kubernetes deployment 66 | uses: benc-uk/workflow-dispatch@v1 67 | with: 68 | token: ${{ env.WORKFLOW_DISPATCH_TOKEN }} 69 | workflow: AWS Kubernetes deployment 70 | - name: Dispatch to danubetech/danubetech-uni-resolver-web 71 | if: contains(github.ref, 'main') 72 | uses: benc-uk/workflow-dispatch@v1 73 | with: 74 | token: ${{ env.WORKFLOW_DISPATCH_TOKEN }} 75 | repo: danubetech/danubetech-uni-resolver-web 76 | workflow: 12455759 # Docker latest image 77 | - name: Slack notification 78 | uses: 8398a7/action-slack@v3 79 | with: 80 | status: ${{ job.status }} 81 | fields: repo,commit,action,eventName,ref,workflow 82 | env: 83 | SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }} 84 | if: failure() 85 | -------------------------------------------------------------------------------- /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 | /* 12 | * From DID Core 13 | */ 14 | 15 | public static final String ERROR_INVALIDDIDURL = "invalidDidUrl"; 16 | public static final String ERROR_NOTFOUND = "notFound"; 17 | 18 | /* 19 | * From DID Resolution 20 | */ 21 | 22 | public static final String ERROR_CONTENTTYEPNOTSUPPORTED = "contentTypeNotSupported"; 23 | public static final String ERROR_INTERNALERROR = "internalError"; 24 | 25 | private static final Logger log = LoggerFactory.getLogger(DereferencingException.class); 26 | 27 | private final String error; 28 | private final Map dereferencingMetadata; 29 | 30 | private final DereferenceResult dereferenceResult; 31 | 32 | public DereferencingException(String error, String message, Map dereferencingMetadata, Throwable ex) { 33 | super(message, ex); 34 | this.error = error; 35 | this.dereferencingMetadata = dereferencingMetadata; 36 | this.dereferenceResult = null; 37 | } 38 | 39 | public DereferencingException(String error, String message, Map dereferencingMetadata) { 40 | super(message); 41 | this.error = error; 42 | this.dereferencingMetadata = dereferencingMetadata; 43 | this.dereferenceResult = null; 44 | } 45 | 46 | public DereferencingException(String error, String message, Throwable ex) { 47 | this(error, message, null, ex); 48 | } 49 | 50 | public DereferencingException(String error, String message) { 51 | this(error, message, null, null); 52 | } 53 | 54 | public DereferencingException(String message, Throwable ex) { 55 | this(ERROR_INTERNALERROR, message, ex); 56 | } 57 | 58 | public DereferencingException(String message) { 59 | this(ERROR_INTERNALERROR, message); 60 | } 61 | 62 | public DereferencingException(DereferenceResult dereferenceResult) { 63 | super(dereferenceResult.getErrorMessage()); 64 | if (! dereferenceResult.isErrorResult()) throw new IllegalArgumentException("No error result: " + dereferenceResult); 65 | this.error = dereferenceResult.getError(); 66 | this.dereferencingMetadata = dereferenceResult.getDereferencingMetadata(); 67 | this.dereferenceResult = dereferenceResult; 68 | } 69 | 70 | /* 71 | * Error methods 72 | */ 73 | 74 | public DereferenceResult toErrorResult(String contentType) { 75 | if (this.getDereferenceResult() != null) { 76 | return this.getDereferenceResult(); 77 | } else { 78 | DereferenceResult dereferenceResult = DereferenceResult.build(); 79 | if (this.getError() != null) dereferenceResult.setError(this.getError()); 80 | if (this.getMessage() != null) dereferenceResult.setErrorMessage(this.getMessage()); 81 | if (this.getDereferencingMetadata() != null) dereferenceResult.getDereferencingMetadata().putAll(this.getDereferencingMetadata()); 82 | dereferenceResult.setContentStream(new byte[0]); 83 | dereferenceResult.setContentType(contentType); 84 | if (log.isDebugEnabled()) log.debug("Created error dereference result: " + dereferenceResult); 85 | return dereferenceResult; 86 | } 87 | } 88 | 89 | /* 90 | * Getters and setters 91 | */ 92 | 93 | public String getError() { 94 | return this.error; 95 | } 96 | 97 | public Map getDereferencingMetadata() { 98 | return dereferencingMetadata; 99 | } 100 | 101 | public DereferenceResult getDereferenceResult() { 102 | return dereferenceResult; 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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.19-SNAPSHOT 12 | 13 | 14 | 15 | 3.2.4 16 | 1.12.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-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-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.19-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/result/ResolveDataModelResult.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 foundation.identity.did.DIDDocument; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import uniresolver.ResolutionException; 11 | 12 | import java.io.IOException; 13 | import java.io.Reader; 14 | import java.util.LinkedHashMap; 15 | import java.util.Map; 16 | 17 | @JsonPropertyOrder({ "didResolutionMetadata", "didDocument", "didDocumentMetadata" }) 18 | @JsonIgnoreProperties(ignoreUnknown=true) 19 | public class ResolveDataModelResult extends ResolveResult implements Result { 20 | 21 | private static final Logger log = LoggerFactory.getLogger(ResolveDataModelResult.class); 22 | 23 | private static final ObjectMapper objectMapper = new ObjectMapper(); 24 | 25 | @JsonProperty("didDocument") 26 | private DIDDocument didDocument; 27 | 28 | private ResolveDataModelResult(Map didResolutionMetadata, DIDDocument didDocument, Map didDocumentMetadata) { 29 | super(didResolutionMetadata, didDocumentMetadata); 30 | this.didDocument = didDocument; 31 | } 32 | 33 | @Override 34 | @JsonIgnore 35 | public boolean isComplete() { 36 | return this.getDidDocument() != null; 37 | } 38 | 39 | /* 40 | * Factory methods 41 | */ 42 | 43 | @JsonCreator 44 | public static ResolveDataModelResult build(@JsonProperty(value="didResolutionMetadata") Map didResolutionMetadata, @JsonProperty(value="didDocument") DIDDocument didDocument, @JsonProperty(value="didDocumentMetadata") Map didDocumentMetadata) { 45 | return new ResolveDataModelResult(didResolutionMetadata, didDocument, didDocumentMetadata); 46 | } 47 | 48 | public static ResolveDataModelResult build() { 49 | return new ResolveDataModelResult(new LinkedHashMap<>(), null, new LinkedHashMap<>()); 50 | } 51 | 52 | /* 53 | * Serialization 54 | */ 55 | 56 | public static ResolveResult fromJson(String json) throws IOException { 57 | return objectMapper.readValue(json, ResolveResult.class); 58 | } 59 | 60 | public static ResolveResult fromJson(Reader reader) throws IOException { 61 | return objectMapper.readValue(reader, ResolveResult.class); 62 | } 63 | 64 | private static boolean isJson(byte[] bytes) { 65 | try { 66 | try (JsonParser jsonParser = objectMapper.getFactory().createParser(bytes)) { 67 | return jsonParser.readValueAsTree() != null; 68 | } 69 | } catch (IOException ex) { 70 | return false; 71 | } 72 | } 73 | 74 | @Override 75 | public Map toMap() { 76 | return objectMapper.convertValue(this, LinkedHashMap.class); 77 | } 78 | 79 | @Override 80 | public String toJson() { 81 | try { 82 | return objectMapper.writeValueAsString(this); 83 | } catch (JsonProcessingException ex) { 84 | throw new RuntimeException("Cannot write JSON: " + ex.getMessage(), ex); 85 | } 86 | } 87 | 88 | /* 89 | * Conversion 90 | */ 91 | 92 | @Override 93 | public void updateConversion() throws ResolutionException { 94 | 95 | for (Map.Entry resolveRepresentationResultEntry : this.resolveRepresentationResults.entrySet()) { 96 | 97 | String mediaType = resolveRepresentationResultEntry.getKey(); 98 | ResolveRepresentationResult resolveRepresentationResult = resolveRepresentationResultEntry.getValue(); 99 | ResolveRepresentationResult newResolveRepresentationResult = Conversion.convertToResolveRepresentationResult(this, mediaType); 100 | resolveRepresentationResult.setDidDocumentStream(newResolveRepresentationResult.getDidDocumentStream()); 101 | } 102 | } 103 | 104 | /* 105 | * Getters and setters 106 | */ 107 | 108 | @JsonRawValue 109 | public final DIDDocument getDidDocument() { 110 | return this.didDocument; 111 | } 112 | 113 | @JsonSetter 114 | public final void setDidDocument(DIDDocument didDocument) { 115 | this.didDocument = didDocument; 116 | } 117 | 118 | /* 119 | * Object methods 120 | */ 121 | 122 | @Override 123 | public String toString() { 124 | return this.toJson(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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_smrNodeEndpoint=https://api.shimmer.network/ 62 | uniresolver_driver_did_iota_customNetworkName=rms 63 | uniresolver_driver_did_iota_customNodeEndpoint=https://api.testnet.shimmer.network/ 64 | -------------------------------------------------------------------------------- /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.ResolveDataModelResult; 6 | import uniresolver.result.ResolveRepresentationResult; 7 | import uniresolver.result.ResolveResult; 8 | 9 | import java.util.Map; 10 | 11 | public class ResolutionException extends Exception { 12 | 13 | /* 14 | * From DID Core 15 | */ 16 | 17 | public static final String ERROR_INVALIDDID = "invalidDid"; 18 | public static final String ERROR_NOTFOUND = "notFound"; 19 | public static final String ERROR_REPRESENTATIONNOTSUPPORTED = "representationNotSupported"; 20 | 21 | /* 22 | * From DID Resolution 23 | */ 24 | 25 | public static final String ERROR_METHODNOTSUPPORTED = "methodNotSupported"; 26 | public static final String ERROR_INTERNALERROR = "internalError"; 27 | 28 | private static final Logger log = LoggerFactory.getLogger(ResolutionException.class); 29 | 30 | private final String error; 31 | private final Map didResolutionMetadata; 32 | 33 | private final ResolveResult resolveResult; 34 | 35 | public ResolutionException(String error, String message, Map didResolutionMetadata, Throwable ex) { 36 | super(message, ex); 37 | this.error = error; 38 | this.didResolutionMetadata = didResolutionMetadata; 39 | this.resolveResult = null; 40 | } 41 | 42 | public ResolutionException(String error, String message, Map didResolutionMetadata) { 43 | super(message); 44 | this.error = error; 45 | this.didResolutionMetadata = didResolutionMetadata; 46 | this.resolveResult = null; 47 | } 48 | 49 | public ResolutionException(String error, String message, Throwable ex) { 50 | this(error, message, null, ex); 51 | } 52 | 53 | public ResolutionException(String error, String message) { 54 | this(error, message, null, null); 55 | } 56 | 57 | public ResolutionException(String message, Throwable ex) { 58 | this(ERROR_INTERNALERROR, message, ex); 59 | } 60 | 61 | public ResolutionException(String message) { 62 | this(ERROR_INTERNALERROR, message); 63 | } 64 | 65 | public ResolutionException(Throwable ex) { 66 | this(ex.getMessage(), ex); 67 | } 68 | 69 | public ResolutionException(ResolveResult resolveResult) { 70 | super(resolveResult.getErrorMessage()); 71 | if (! resolveResult.isErrorResult()) throw new IllegalArgumentException("No error result: " + resolveResult); 72 | this.error = resolveResult.getError(); 73 | this.didResolutionMetadata = resolveResult.getDidResolutionMetadata(); 74 | this.resolveResult = resolveResult; 75 | } 76 | 77 | /* 78 | * Error methods 79 | */ 80 | 81 | public ResolveDataModelResult toErrorResult() { 82 | if (this.getResolveResult() != null) { 83 | try { 84 | return this.getResolveResult().toResolveDataModelResult(); 85 | } catch (ResolutionException ex) { 86 | throw new IllegalStateException(ex.getMessage(), ex); 87 | } 88 | } else { 89 | ResolveDataModelResult resolveDataModelResult = ResolveDataModelResult.build(); 90 | if (this.getError() != null) resolveDataModelResult.setError(this.getError()); 91 | if (this.getMessage() != null) resolveDataModelResult.setErrorMessage(this.getMessage()); 92 | if (this.getDidResolutionMetadata() != null) resolveDataModelResult.getDidResolutionMetadata().putAll(this.getDidResolutionMetadata()); 93 | resolveDataModelResult.setDidDocument(null); 94 | if (log.isDebugEnabled()) log.debug("Created error resolve result: " + resolveDataModelResult); 95 | return resolveDataModelResult; 96 | } 97 | } 98 | 99 | public ResolveRepresentationResult toErrorResult(String contentType) { 100 | if (this.getResolveResult() != null) { 101 | try { 102 | return this.getResolveResult().toResolveRepresentationResult(contentType); 103 | } catch (ResolutionException ex) { 104 | throw new IllegalStateException(ex.getMessage(), ex); 105 | } 106 | } else { 107 | ResolveRepresentationResult resolveRepresentationResult = ResolveRepresentationResult.build(); 108 | if (this.getError() != null) resolveRepresentationResult.setError(this.getError()); 109 | if (this.getMessage() != null) resolveRepresentationResult.setErrorMessage(this.getMessage()); 110 | if (this.getDidResolutionMetadata() != null) resolveRepresentationResult.setDidResolutionMetadata(this.getDidResolutionMetadata()); 111 | resolveRepresentationResult.setDidDocumentStream(new byte[0]); 112 | resolveRepresentationResult.setContentType(contentType); 113 | if (log.isDebugEnabled()) log.debug("Created error resolve result: " + resolveRepresentationResult); 114 | return resolveRepresentationResult; 115 | } 116 | } 117 | 118 | /* 119 | * Getters and setters 120 | */ 121 | 122 | public String getError() { 123 | return error; 124 | } 125 | 126 | public Map getDidResolutionMetadata() { 127 | return didResolutionMetadata; 128 | } 129 | 130 | public ResolveResult getResolveResult() { 131 | return resolveResult; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /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.MethodsServlet; 14 | import uniresolver.web.servlet.PropertiesServlet; 15 | import uniresolver.web.servlet.ResolveServlet; 16 | import uniresolver.web.servlet.TestIdentifiersServlet; 17 | 18 | import java.net.URI; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | @Configuration 23 | public class WebAppConfig { 24 | 25 | private static final Logger log = LogManager.getLogger(WebAppConfig.class); 26 | 27 | @Autowired 28 | private DriverConfigs driverConfigs; 29 | 30 | @Autowired 31 | private ServletMappings servletMappings; 32 | 33 | @Autowired 34 | private LocalUniResolver localUniResolver; 35 | 36 | @Bean(name = "ResolveServlet") 37 | public ResolveServlet resolveServlet() { 38 | return new ResolveServlet(); 39 | } 40 | 41 | @Bean 42 | public ServletRegistrationBean propertiesServletRegistrationBean() { 43 | return new ServletRegistrationBean<>(propertiesServlet(), fixWildcardPattern(servletMappings.getProperties())); 44 | } 45 | 46 | @Bean(name = "PropertiesServlet") 47 | public PropertiesServlet propertiesServlet() { 48 | return new PropertiesServlet(); 49 | } 50 | 51 | @Bean 52 | public ServletRegistrationBean resolveServletRegistrationBean() { 53 | return new ServletRegistrationBean<>(resolveServlet(), fixWildcardPattern(servletMappings.getResolve())); 54 | } 55 | 56 | @Bean(name = "MethodServlet") 57 | public MethodsServlet methodsServlet() { 58 | return new MethodsServlet(); 59 | } 60 | 61 | @Bean 62 | public ServletRegistrationBean methodServletRegistrationBean() { 63 | return new ServletRegistrationBean<>(methodsServlet(), fixWildcardPattern(servletMappings.getMethods())); 64 | } 65 | 66 | @Bean(name = "TestIdentifiersServlet") 67 | public TestIdentifiersServlet testIdentifiersServlet() { 68 | return new TestIdentifiersServlet(); 69 | } 70 | 71 | @Bean 72 | public ServletRegistrationBean testIdentifiersServletRegistrationBean() { 73 | return new ServletRegistrationBean<>(testIdentifiersServlet(), servletMappings.getTestIdentifiers()); 74 | } 75 | 76 | public static String fixWildcardPattern(String s) { 77 | if(s == null) return ""; 78 | if (s.endsWith("*")) return s; 79 | if (s.endsWith("/")) return s + "*"; 80 | return s + "/*"; 81 | } 82 | 83 | public static String normalizeUri(String s, boolean postSlash) { 84 | if (s == null) return null; 85 | String url = s; 86 | if (url.endsWith("*")) url = url.substring(0, url.length() - 1); 87 | 88 | URI uri = URI.create(url + "/").normalize(); 89 | 90 | return postSlash ? uri.toString() : uri.toString().substring(0, uri.toString().length() - 1); 91 | } 92 | 93 | public void configureLocalUniresolver(DriverConfigs driverConfigs, LocalUniResolver uniResolver) { 94 | 95 | List drivers = new ArrayList<>(); 96 | 97 | for (DriverConfigs.DriverConfig dc : driverConfigs.getDrivers()) { 98 | 99 | String pattern = dc.getPattern(); 100 | String url = dc.getUrl(); 101 | String propertiesEndpoint = dc.getPropertiesEndpoint(); 102 | List testIdentifiers = dc.getTestIdentifiers(); 103 | 104 | if (pattern == null) throw new IllegalArgumentException("Missing 'pattern' entry in driver configuration."); 105 | if (url == null) throw new IllegalArgumentException("Missing 'url' entry in driver configuration."); 106 | 107 | // construct HTTP driver 108 | 109 | HttpDriver driver = new HttpDriver(); 110 | driver.setPattern(pattern); 111 | 112 | if (url.contains("$1") || url.contains("$2")) { 113 | driver.setResolveUri(url); 114 | driver.setPropertiesUri((URI) null); 115 | } else { 116 | if (! url.endsWith("/")) url = url + "/"; 117 | driver.setResolveUri(normalizeUri((url + servletMappings.getResolve()), true)); 118 | if ("true".equals(propertiesEndpoint)) driver.setPropertiesUri(normalizeUri((url + servletMappings.getProperties()), false)); 119 | } 120 | 121 | driver.setTestIdentifiers(testIdentifiers); 122 | 123 | // done 124 | 125 | drivers.add(driver); 126 | if (log.isInfoEnabled()) log.info("Added driver for pattern '" + dc.getPattern() + "' at " + driver.getResolveUri() + " (" + driver.getPropertiesUri() + ")"); 127 | } 128 | 129 | uniResolver.setDrivers(drivers); 130 | } 131 | 132 | @PostConstruct 133 | private void initDrivers() { 134 | if (driverConfigs.getDrivers() != null) configureLocalUniresolver(driverConfigs, localUniResolver); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /driver/src/main/java/uniresolver/driver/servlet/ResolveServlet.java: -------------------------------------------------------------------------------- 1 | package uniresolver.driver.servlet; 2 | 3 | import foundation.identity.did.DID; 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 org.springframework.http.MediaType; 11 | import uniresolver.ResolutionException; 12 | import uniresolver.driver.util.HttpBindingServerUtil; 13 | import uniresolver.driver.util.MediaTypeUtil; 14 | import uniresolver.result.ResolveRepresentationResult; 15 | import uniresolver.result.ResolveResult; 16 | 17 | import java.io.IOException; 18 | import java.net.URLDecoder; 19 | import java.nio.charset.StandardCharsets; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | public class ResolveServlet extends HttpServlet implements Servlet { 25 | 26 | private static final Logger log = LoggerFactory.getLogger(ResolveServlet.class); 27 | 28 | public ResolveServlet() { 29 | 30 | super(); 31 | } 32 | 33 | @Override 34 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 35 | 36 | // read request 37 | 38 | request.setCharacterEncoding("UTF-8"); 39 | response.setCharacterEncoding("UTF-8"); 40 | 41 | String contextPath = request.getContextPath(); 42 | String servletPath = request.getServletPath(); 43 | String requestPath = request.getRequestURI(); 44 | 45 | String identifier = requestPath.substring(contextPath.length() + servletPath.length()); 46 | if (identifier.startsWith("/")) identifier = identifier.substring(1); 47 | identifier = URLDecoder.decode(identifier, StandardCharsets.UTF_8); 48 | 49 | if (log.isInfoEnabled()) log.info("Driver: Incoming resolve request for identifier: " + identifier); 50 | 51 | if (identifier == null) { 52 | 53 | ServletUtil.sendResponse(response, HttpServletResponse.SC_BAD_REQUEST, "Driver: No identifier found in resolve request."); 54 | return; 55 | } 56 | 57 | // assume identifier is a DID 58 | 59 | String didString = identifier; 60 | 61 | // prepare resolution options 62 | 63 | String httpAcceptHeader = request.getHeader("Accept"); 64 | if (log.isInfoEnabled()) log.info("Driver: Incoming Accept: header string: " + httpAcceptHeader); 65 | 66 | List httpAcceptMediaTypes = MediaType.parseMediaTypes(httpAcceptHeader != null ? httpAcceptHeader : ResolveResult.MEDIA_TYPE); 67 | MediaType.sortBySpecificityAndQuality(httpAcceptMediaTypes); 68 | 69 | String accept = HttpBindingServerUtil.acceptForHttpAccepts(httpAcceptMediaTypes); 70 | 71 | Map resolutionOptions = new HashMap<>(); 72 | resolutionOptions.put("accept", accept); 73 | 74 | if (log.isDebugEnabled()) log.debug("Driver: Using resolution options: " + resolutionOptions); 75 | 76 | // invoke the driver 77 | 78 | ResolveRepresentationResult resolveRepresentationResult; 79 | 80 | try { 81 | 82 | resolveRepresentationResult = InitServlet.getDriver().resolveRepresentation(DID.fromString(didString), resolutionOptions); 83 | if (resolveRepresentationResult == null) throw new ResolutionException(ResolutionException.ERROR_NOTFOUND, "Driver: No resolve result for " + didString); 84 | } catch (Exception ex) { 85 | 86 | if (log.isWarnEnabled()) log.warn("Driver: Resolve problem for " + didString + ": " + ex.getMessage(), ex); 87 | 88 | if (! (ex instanceof ResolutionException)) ex = new ResolutionException("Driver: Resolve problem for " + didString + ": " + ex.getMessage()); 89 | resolveRepresentationResult = ((ResolutionException) ex).toErrorResult(accept); 90 | } 91 | 92 | if (log.isInfoEnabled()) log.info("Driver: Resolve result for " + didString + ": " + resolveRepresentationResult); 93 | 94 | // write resolve result 95 | 96 | for (MediaType httpAcceptMediaType : httpAcceptMediaTypes) { 97 | 98 | if (MediaTypeUtil.isMediaTypeAcceptable(httpAcceptMediaType, ResolveResult.MEDIA_TYPE)) { 99 | 100 | if (log.isDebugEnabled()) log.debug("Supporting HTTP media type " + httpAcceptMediaType + " via content type " + ResolveResult.MEDIA_TYPE); 101 | 102 | ServletUtil.sendResponse( 103 | response, 104 | HttpBindingServerUtil.httpStatusCodeForResult(resolveRepresentationResult), 105 | ResolveResult.MEDIA_TYPE, 106 | HttpBindingServerUtil.toHttpBodyStreamResult(resolveRepresentationResult)); 107 | return; 108 | } else { 109 | 110 | // determine representation media type 111 | 112 | if (MediaTypeUtil.isMediaTypeAcceptable(httpAcceptMediaType, resolveRepresentationResult.getContentType())) { 113 | if (log.isDebugEnabled()) log.debug("Supporting HTTP media type " + httpAcceptMediaType + " via content type " + resolveRepresentationResult.getContentType()); 114 | } else { 115 | if (log.isDebugEnabled()) log.debug("Not supporting HTTP media type " + httpAcceptMediaType); 116 | continue; 117 | } 118 | 119 | ServletUtil.sendResponse( 120 | response, 121 | HttpBindingServerUtil.httpStatusCodeForResult(resolveRepresentationResult), 122 | resolveRepresentationResult.getContentType(), 123 | resolveRepresentationResult.getDidDocumentStream() 124 | ); 125 | return; 126 | } 127 | } 128 | 129 | ServletUtil.sendResponse( 130 | response, 131 | HttpServletResponse.SC_NOT_ACCEPTABLE, 132 | "Not acceptable media types " + httpAcceptHeader); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /uni-resolver-core/src/main/java/uniresolver/result/ResolveRepresentationResult.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.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import uniresolver.ResolutionException; 12 | 13 | import java.io.IOException; 14 | import java.io.Reader; 15 | import java.nio.charset.StandardCharsets; 16 | import java.util.LinkedHashMap; 17 | import java.util.Map; 18 | 19 | @JsonPropertyOrder({ "didResolutionMetadata", "didDocumentStream", "didDocumentMetadata" }) 20 | @JsonIgnoreProperties(ignoreUnknown=true) 21 | public class ResolveRepresentationResult extends ResolveResult implements Result, StreamResult { 22 | 23 | private static final Logger log = LoggerFactory.getLogger(ResolveRepresentationResult.class); 24 | 25 | private static final ObjectMapper objectMapper = new ObjectMapper(); 26 | 27 | @JsonProperty("didDocumentStream") 28 | private byte[] didDocumentStream; 29 | 30 | private ResolveRepresentationResult(Map didResolutionMetadata, byte[] didDocumentStream, Map didDocumentMetadata) { 31 | super(didResolutionMetadata, didDocumentMetadata); 32 | this.didDocumentStream = didDocumentStream != null ? didDocumentStream : new byte[0]; 33 | } 34 | 35 | @Override 36 | @JsonIgnore 37 | public boolean isComplete() { 38 | return this.getContentType() != null && this.getDidDocumentStream() != null; 39 | } 40 | 41 | /* 42 | * Factory methods 43 | */ 44 | 45 | @JsonCreator 46 | public static ResolveRepresentationResult build(@JsonProperty(value="didResolutionMetadata") Map didResolutionMetadata, @JsonProperty(value="didDocumentStream") byte[] didDocumentStream, @JsonProperty(value="didDocumentMetadata") Map didDocumentMetadata) { 47 | return new ResolveRepresentationResult(didResolutionMetadata, didDocumentStream, didDocumentMetadata); 48 | } 49 | 50 | public static ResolveRepresentationResult build() { 51 | return new ResolveRepresentationResult(new LinkedHashMap<>(), new byte[0], new LinkedHashMap<>()); 52 | } 53 | 54 | /* 55 | * Serialization 56 | */ 57 | 58 | public static ResolveRepresentationResult fromJson(String json) throws IOException { 59 | return objectMapper.readValue(json, ResolveRepresentationResult.class); 60 | } 61 | 62 | public static ResolveRepresentationResult fromJson(Reader reader) throws IOException { 63 | return objectMapper.readValue(reader, ResolveRepresentationResult.class); 64 | } 65 | 66 | @Override 67 | public Map toMap() { 68 | return objectMapper.convertValue(this, LinkedHashMap.class); 69 | } 70 | 71 | @Override 72 | public String toJson() { 73 | try { 74 | return objectMapper.writeValueAsString(this); 75 | } catch (JsonProcessingException ex) { 76 | throw new RuntimeException("Cannot write JSON: " + ex.getMessage(), ex); 77 | } 78 | } 79 | 80 | /* 81 | * Content stream methods 82 | */ 83 | 84 | @Override 85 | public byte[] getFunctionContentStream() { 86 | return this.getDidDocumentStream(); 87 | } 88 | 89 | @Override 90 | public void setFunctionContentStream(byte[] functionContentStream) { 91 | this.setDidDocumentStream(functionContentStream); 92 | } 93 | 94 | /* 95 | * Conversion 96 | */ 97 | 98 | public DereferenceResult toDereferenceResult() { 99 | DereferenceResult dereferenceResult = DereferenceResult.build(); 100 | dereferenceResult.getDereferencingMetadata().putAll(this.getDidResolutionMetadata()); 101 | dereferenceResult.setContentStream(this.getDidDocumentStream()); 102 | dereferenceResult.getContentMetadata().putAll(this.getDidDocumentMetadata()); 103 | return dereferenceResult; 104 | } 105 | 106 | @Override 107 | public void updateConversion() throws ResolutionException { 108 | 109 | if (this.resolveDataModelResult != null) { 110 | 111 | ResolveDataModelResult newResolveDataModelResult = Conversion.convertToResolveDataModelResult(this); 112 | this.resolveDataModelResult.setDidDocument(newResolveDataModelResult.getDidDocument()); 113 | } 114 | } 115 | 116 | /* 117 | * Helper methods 118 | */ 119 | 120 | private static boolean isJson(byte[] bytes) { 121 | try { 122 | try (JsonParser jsonParser = objectMapper.getFactory().createParser(bytes)) { 123 | return jsonParser.readValueAsTree() != null; 124 | } 125 | } catch (IOException ex) { 126 | return false; 127 | } 128 | } 129 | 130 | /* 131 | * Getters and setters 132 | */ 133 | 134 | @JsonIgnore 135 | public final byte[] getDidDocumentStream() { 136 | return this.didDocumentStream; 137 | } 138 | 139 | @JsonGetter("didDocumentStream") 140 | public final String getDidDocumentStreamAsString() { 141 | if (this.getDidDocumentStream() == null) { 142 | return null; 143 | } else { 144 | if (isJson(this.getDidDocumentStream())) { 145 | return new String(this.getDidDocumentStream(), StandardCharsets.UTF_8); 146 | } else { 147 | return Hex.encodeHexString(this.getDidDocumentStream()); 148 | } 149 | } 150 | } 151 | 152 | @JsonIgnore 153 | public final void setDidDocumentStream(byte[] didDocumentStream) { 154 | this.didDocumentStream = didDocumentStream; 155 | } 156 | 157 | @JsonSetter("didDocumentStream") 158 | public final void setDidDocumentStreamAsString(String didDocumentStream) throws DecoderException { 159 | if (didDocumentStream == null) { 160 | this.setDidDocumentStream(null); 161 | } else { 162 | try { 163 | this.setDidDocumentStream(Hex.decodeHex(didDocumentStream)); 164 | } catch (DecoderException ex) { 165 | this.setDidDocumentStream(didDocumentStream.getBytes(StandardCharsets.UTF_8)); 166 | } 167 | } 168 | } 169 | 170 | /* 171 | * Object methods 172 | */ 173 | 174 | @Override 175 | public String toString() { 176 | return this.toJson(); 177 | } 178 | } 179 | --------------------------------------------------------------------------------