diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index ac16682..0000000 --- a/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -**/*.env -node_modules diff --git a/.env.example b/.env.example deleted file mode 100644 index 58a9199..0000000 --- a/.env.example +++ /dev/null @@ -1,26 +0,0 @@ -# see the README for an explanation of all env values - -# General environment variables -CRED_STATUS_SERVICE=mongodb -CRED_STATUS_DID_SEED=z1AackbUm8U69ohKnihoRRFkXcXJd4Ra1PkAboQ2ZRy1ngB -PORT=4008 # default port is 4008 -ENABLE_ACCESS_LOGGING=true -ENABLE_HTTPS_FOR_DEV=false -ERROR_LOG_FILE=logs/error.log -ALL_LOG_FILE=logs/all.log -CONSOLE_LOG_LEVEL=silly # default is silly, i.e. log everything - see the README for allowed levels -LOG_LEVEL=silly # default is silly - -# Database specific environment variables -STATUS_CRED_SITE_ORIGIN=https://credentials.example.edu -CRED_STATUS_DB_URL=mongodb+srv://user:pass@domain.mongodb.net?retryWrites=false -CRED_STATUS_DB_HOST=domain.mongodb.net # ignored if CRED_STATUS_DB_URL is configured -CRED_STATUS_DB_PORT=27017 # ignored if CRED_STATUS_DB_URL is configured -CRED_STATUS_DB_USER=testuser # ignored if CRED_STATUS_DB_URL is configured -CRED_STATUS_DB_PASS=testpass # ignored if CRED_STATUS_DB_URL is configured -CRED_STATUS_DB_NAME= # autogenerated if omitted -STATUS_CRED_TABLE_NAME= # autogenerated if omitted -USER_CRED_TABLE_NAME= # autogenerated if omitted -CONFIG_TABLE_NAME= # autogenerated if omitted -EVENT_TABLE_NAME= # autogenerated if omitted -CRED_EVENT_TABLE_NAME= # autogenerated if omitted diff --git a/.github/workflows/issues-to-project.yml b/.github/workflows/issues-to-project.yml deleted file mode 100644 index f0a2c40..0000000 --- a/.github/workflows/issues-to-project.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: move new, edited, reopened issues to DCC Engineering project - -on: - issues: - types: [ opened, edited, reopened ] -jobs: - add-to-project: - name: Add issue to project - runs-on: ubuntu-latest - steps: - - uses: actions/add-to-project@main - with: - project-url: https://github.com/orgs/digitalcredentials/projects/14 - github-token: ${{ secrets.PROJECTS_ACCESS_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index abf96bb..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Node.js CI - -on: [push] - -jobs: - test-node: - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - matrix: - node-version: [20.x] - steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - name: Run test with Node.js ${{ matrix.node-version }} - run: npm run test - env: - CI: true diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3e82971..0000000 --- a/.gitignore +++ /dev/null @@ -1,111 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Library lock files -package-lock.json -yarn.lock - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# vscode -.vscode \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index c095880..0000000 --- a/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM node:18-alpine -WORKDIR /app -COPY . . -RUN npm install -CMD ["node", "server.js"] -EXPOSE 4008 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 53834fc..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Digital Credentials Consortium - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 01079dd..5858dfb 100644 --- a/README.md +++ b/README.md @@ -1,345 +1,2 @@ -# Digital Credentials Consortium Database Verifiable Credential Status Service - -[![Build status](https://img.shields.io/github/actions/workflow/status/digitalcredentials/status-service-db/main.yml?branch=main)](https://github.com/digitalcredentials/status-service-db/actions?query=workflow%3A%22Node.js+CI%22) - -IMPORTANT NOTE ABOUT VERSIONING: If you are using a Docker Hub image of this repository, make sure you are reading the version of this README that corresponds to your Docker Hub version. If, for example, you are using the image `digitalcredentials/status-service-db:0.1.0` then you'll want to use the corresponding tagged repo: [https://github.com/digitalcredentials/status-service-db/tree/v0.1.0](https://github.com/digitalcredentials/status-service-db/tree/v0.1.0). - -## Table of Contents - -- [Summary](#summary) -- [Environment Variables](#environment-variables) -- [Signing Key](#signing-key) - - [DID Registries](#did-registries) -- [Usage](#usage) - - [Allocate Status Position](#allocate-status-position) - - [Revocation and Suspension](#revocation-and-suspension) -- [Versioning](#versioning) -- [Logging](#logging) - - [Log Levels](#log-levels) - - [Access Logging](#access-logging) -- [Development](#development) - - [Testing](#testing) -- [Contribute](#contribute) -- [License](#license) - -## Summary - -A microservice (running as a nodejs express app) that uses a database service to allocate a [status position](https://www.w3.org/TR/vc-bitstring-status-list) for a [Verifiable Credential](https://www.w3.org/TR/vc-data-model-2.0), adds the position to the credential, and returns the credential. The status position can later be used to revoke the credential. - -Implements two HTTP endpoints: - -- [POST /credentials/status/allocate](https://w3c-ccg.github.io/vc-api/#issue-credential) -- [POST /credentials/status](https://w3c-ccg.github.io/vc-api/#update-status) - -The `/credentials/status` endpoint corresponds to the [VC-API /credentials/status endpoint](https://w3c-ccg.github.io/vc-api#update-status) - -## Environment Variables - -This service provides support for managing credential status in a variety of database services. Currently, it supports integration with MongoDB via [this implementation](https://github.com/digitalcredentials/credential-status-manager-db) of [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list). We have provided a sample `.env.example` file that you can use as a guide for creating a `.env` file for your implementation. Here are the variables recognized by all database credential status managers: - -| Key | Description | Type | Required | -| --- | --- | --- | --- | -| `CRED_STATUS_SERVICE` | name of the database service used to manage credential status data | `mongodb` | yes | -| \* `STATUS_CRED_SITE_ORIGIN` | base URL of status credentials managed by a given deployment | string | yes | -| `CRED_STATUS_DB_URL` | URL of the database instance used to manage credential status data | string | yes if the other set of `CRED_STATUS_DB_*` fields are not set | -| `CRED_STATUS_DB_HOST` | host of the database instance used to manage credential status data | string | yes if `CRED_STATUS_DB_URL` is not set | -| `CRED_STATUS_DB_PORT` | port of the database instance used to manage credential status data | number | yes if `CRED_STATUS_DB_URL` is not set | -| `CRED_STATUS_DB_USER` | username of user with read/write privileges on the database instance used to manage credential status data | string | yes if `CRED_STATUS_DB_URL` is not set | -| `CRED_STATUS_DB_PASS` | password associated with `CRED_STATUS_DB_USER` | string | yes if `CRED_STATUS_DB_URL` is not set | -| `CRED_STATUS_DB_NAME` | name of the database instance used to manage credential status data | string | no (default: `credentialStatus`) | -| `STATUS_CRED_TABLE_NAME` | name of the database table used to manage status credentials | string | no (default: `StatusCredential`) | -| `USER_CRED_TABLE_NAME` | name of the database table used to manage user credentials | string | no (default: `UserCredential`) | -| `CONFIG_TABLE_NAME` | name of the database table used to manage application configuration | string | no (default: `Config`) | -| `EVENT_TABLE_NAME` | name of the database table used to manage credential status events | string | no (default: `Event`) | -| `CRED_EVENT_TABLE_NAME` | name of the database table used to manage the latest status event for a given credential | string | no (default: `CredentialEvent`) | -| `CRED_STATUS_DID_SEED` | seed used to deterministically generate DID | string | yes | -| `PORT` | HTTP port on which to run the express app | number | no (default: `4008`) | -| `ENABLE_ACCESS_LOGGING` | whether to enable access logging (see [Logging](#logging)) | boolean | no (default: `true`) | -| `ENABLE_HTTPS_FOR_DEV` | whether to enable HTTPS in a development instance of the app | boolean | no (default: `true`) | -| `ERROR_LOG_FILE` | log file for all errors (see [Logging](#logging)) | string | no | -| `ALL_LOG_FILE` | log file for everything (see [Logging](#logging)) | string | no | -| `CONSOLE_LOG_LEVEL` | console log level (see [Logging](#logging)) | `error` \| `warn`\| `info` \| `http` \| `verbose` \| `debug` \| `silly` | no (default: `silly`) | -| `LOG_LEVEL` | log level for application (see [Logging](#logging)) | `error` \| `warn`\| `info` \| `http` \| `verbose` \| `debug` \| `silly` | no (default: `silly`) | - -\* See [Usage](#usage) for guidance on setting a test value for `STATUS_CRED_SITE_ORIGIN`. - -## Signing Key - -`status-service-db` is configured with a default signing key that can only be used for testing and evaluation. - -In production, you must generate your own signing key and assign it to the `CRED_STATUS_DID_SEED` environment variable. An easy-ish way to generate a new key is explained [here](https://github.com/digitalcredentials/issuer-coordinator#generate-a-new-key). Those instructions will give you a JSON object with a `seed` property. Copy the value of that property and assign it to `CRED_STATUS_DID_SEED`. - -### DID Registries - -So that a verifier knows that the status list was signed by a key that is really owned by the claimed issuer, the key (encoded as a DID) has to be confirmed as really belonging to that issuer. This is typically done by adding the DID to a well known registry that the verifier checks when verifying a credential. - -The DCC provides a number of registries that work with the verifiers in the Learner Credential Wallet and in the online web based [Verifier Plus](https://verifierplus.org). The DCC registries use GitHub for storage. To request that your DID be added to a registry, submit a pull request in which you've added your [DID](https://www.w3.org/TR/did-core) to the registry file. - -## Usage - -The `/credentials/status/allocate` HTTP endpoint is meant to be called from any software wanting to allocate a position, particularly by the [DCC issuer-coordinator](https://github.com/digitalcredentials/issuer-coordinator) from within a Docker Compose network. - -This express app can be run a few different ways: - -- 1 with with the `start` script in package.json -- 2 directly from the Docker Hub image: `docker run -dp 4008:4008 digitalcredentials/status-service-db:0.1.0` -- 2 with Docker Compose - see how we do that in the [DCC issuer-coordinator](https://github.com/digitalcredentials/issuer-coordinator) - -1 In order for credential status verification to work, you will need to use a publicly accessible URL for `STATUS_CRED_SITE_ORIGIN`, so that the verifier can access the status data. If you would like to spin up this service at a public URL, consider using a traffic forwarding tool like [localtunnel](https://www.npmjs.com/package/localtunnel). After you have installed it on your computer with `npm install -g localtunnel`, please follow these simple steps to run the service: - -1. Run `npm run lt` (assuming you have already set the `PORT` environment variable) -2. Visit the URL that is logged to the terminal and enter the tunnel password (typically your public IP address, which can be found at sites like [ip.me](https://ip.me)) -3. Set the `STATUS_CRED_SITE_ORIGIN` environment variable to the URL generated from Step 2 (be sure to also configure all other required environment variables) -4. Run `npm start` (assuming you have already run `npm install`) - -2 Note that to run this with Docker, you'll of course need to install Docker, which is very easy with the [Docker installers for Windows, Mac, and Linux](https://docs.docker.com/engine/install). - -### Allocate Status Position - -You can now allocate status positions for Verifiable Credentials. Try it out with this cURL command, which you simply paste into the terminal: - -```bash -curl --location 'http://localhost:4008/credentials/status/allocate' \ ---header 'Content-Type: application/json' \ ---data-raw '{ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json" - ], - "id": "urn:uuid:2fe53dc9-b2ec-4939-9b2c-0d00f6663b6c", - "type": [ - "VerifiableCredential", - "OpenBadgeCredential" - ], - "name": "DCC Test Credential", - "issuer": { - "type": [ - "Profile" - ], - "id": "did:key:z6MknNQD1WHLGGraFi6zcbGevuAgkVfdyCdtZnQTGWVVvR5Q", - "name": "Digital Credentials Consortium Test Issuer", - "url": "https://dcconsortium.org", - "image": "https://user-images.githubusercontent.com/752326/230469660-8f80d264-eccf-4edd-8e50-ea634d407778.png" - }, - "validFrom": "2023-08-02T17:43:32.903Z", - "credentialSubject": { - "type": [ - "AchievementSubject" - ], - "achievement": { - "id": "urn:uuid:bd6d9316-f7ae-4073-a1e5-2f7f5bd22922", - "type": [ - "Achievement" - ], - "achievementType": "Diploma", - "name": "Badge", - "description": "This is a sample credential issued by the Digital Credentials Consortium to demonstrate the functionality of Verifiable Credentials for wallets and verifiers.", - "criteria": { - "type": "Criteria", - "narrative": "This credential was issued to a student that demonstrated proficiency in the Python programming language that occurred from **February 17, 2023** to **June 12, 2023**." - }, - "image": { - "id": "https://user-images.githubusercontent.com/752326/214947713-15826a3a-b5ac-4fba-8d4a-884b60cb7157.png", - "type": "Image" - } - }, - "name": "Jane Doe" - } -}' -``` - -This should return the same credential but with an allocated status. It should look something like this (it will be all smushed up, but you can format it in something like [JSONLint](https://jsonlint.com)): - -```json -{ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json", - "https://w3id.org/security/suites/ed25519-2020/v1" - ], - "id": "urn:uuid:2fe53dc9-b2ec-4939-9b2c-0d00f6663b6c", - "type": [ - "VerifiableCredential", - "OpenBadgeCredential" - ], - "name": "DCC Test Credential", - "issuer": { - "type": [ - "Profile" - ], - "id": "did:key:z6MknNQD1WHLGGraFi6zcbGevuAgkVfdyCdtZnQTGWVVvR5Q", - "name": "Digital Credentials Consortium Test Issuer", - "url": "https://dcconsortium.org", - "image": "https://user-images.githubusercontent.com/752326/230469660-8f80d264-eccf-4edd-8e50-ea634d407778.png" - }, - "validFrom": "2023-08-02T17:43:32.903Z", - "credentialSubject": { - "type": [ - "AchievementSubject" - ], - "achievement": { - "id": "urn:uuid:bd6d9316-f7ae-4073-a1e5-2f7f5bd22922", - "type": [ - "Achievement" - ], - "achievementType": "Diploma", - "name": "Badge", - "description": "This is a sample credential issued by the Digital Credentials Consortium to demonstrate the functionality of Verifiable Credentials for wallets and verifiers.", - "criteria": { - "type": "Criteria", - "narrative": "This credential was issued to a student that demonstrated proficiency in the Python programming language that occurred from **February 17, 2023** to **June 12, 2023**." - }, - "image": { - "id": "https://user-images.githubusercontent.com/752326/214947713-15826a3a-b5ac-4fba-8d4a-884b60cb7157.png", - "type": "Image" - } - }, - "name": "Jane Doe" - }, - "credentialStatus": [ - { - "id": "https://digitalcredentials.github.io/credential-status-jc-test/XA5AAK1PV4#2", - "type": "BitstringStatusListEntry", - "statusPurpose": "revocation", - "statusListIndex": 2, - "statusListCredential": "https://digitalcredentials.github.io/credential-status-jc-test/XA5AAK1PV4" - }, - { - "id": "https://digitalcredentials.github.io/credential-status-jc-test/DKSPRCX9WB#5", - "type": "BitstringStatusListEntry", - "statusPurpose": "suspension", - "statusListIndex": 5, - "statusListCredential": "https://digitalcredentials.github.io/credential-status-jc-test/DKSPRCX9WB" - } - ] -} -``` - -Now, your next step would be to sign this Verifiable Credential. You could pass the VC (with its newly allocated status position) to the [DCC signing-service](https://github.com/digitalcredentials/signing-service), which will sign and return the signed copy. To see how this is can all be coordinated, take a look at the [DCC issuer-coordinator](https://github.com/digitalcredentials/issuer-coordinator). - -NOTE: cURL can get a bit clunky if you want to experiment more (e.g., by changing what goes into the VC before signing), so you might consider trying [Postman](https://www.postman.com/downloads) which makes it easier to construct and send HTTP calls. - -### Revocation and Suspension - -Revocation and suspension are fully explained in the [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list/) specification and our implemenations thereof, but effectively, it amounts to POSTing an object containing the credential ID and the desired status update to the `/credentials/status` endpoint. For example: - -```bash -curl --location 'http://localhost:4008/credentials/status' \ ---header 'Content-Type: application/json' \ ---data-raw '{ - "credentialId": "urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1", - "credentialStatus": [ - { "type": "BitstringStatusListCredential", "status": "revoked" } - ] -} -``` - -## Versioning - -`status-service-db` is primarily intended to run as a Docker image within a Docker Compose network, typically as part of a flow that is orchestrated by the [DCC Issuer Coordinator](https://github.com/digitalcredentials/issuer-coordinator) and the [DCC Workflow Coordinator](https://github.com/digitalcredentials/workflow-coordinator). - -For convenience, we've published the images for the `status-service-db` and the other services used by the coordinators, as well as for the coordinators themselves, to Docker Hub so that you don't have to build them locally yourself from the GitHub repositories. - -The images on Docker Hub will at times be updated to add new functionality and fix bugs. Rather than overwrite the default (`latest`) version on Docker Hub for each update, we've adopted the [Semantic Versioning Guidelines](https://semver.org) with our Docker image tags. - -We DO NOT provide a `latest` tag so you must provide a tag name (i.e, the version number) for the images in your Docker Compose file. - -To ensure you've got compatible versions of the services and the coordinator, the `major` number for each should match. At the time of writing, the versions for each are at 0.1.0, and the `major` number (the leftmost number) agrees across all three. - -If you do ever want to work from the source code in the repository and build your own images, we've tagged the commits in GitHub that were used to build the corresponding Docker image. So a GitHub tag of v0.1.0 coresponds to a Docker image tag of 0.1.0 - -## Logging - -### Log Levels - -We support the following log levels: - -``` - error: 0, - warn: 1, - info: 2, - http: 3, - verbose: 4, - debug: 5, - silly: 6 -``` - -Logging is configured with environment variables, as defined in the [Environment Variables](#environment-variables) section. - -By default, everything is logged to the console (log level `silly`). - -You may set the log level for the application as a whole. For example: - -``` -LOG_LEVEL=http -``` - -Which would only log messages with severity `http` and all below it (`info`, `warn`, `error`). - -The default is to log everything (level `silly`). - -You can also set the log level for console logging. For example: - -``` -CONSOLE_LOG_LEVEL=debug -``` - -This would log everything for severity `debug` and lower (i.e., `verbose`, `http`, `info`, `warn`, `error`). This of course assumes that you've set the log level for the application as a whole to at least the same level. - -The default log level for the console is `silly`, which logs everything. - -There are also two log files that can be enabled: - -- errors (only logs errors) -- all (logs everything - all log levels) - -Enable each log by setting an environment variable for each, indicating the path to the appropriate file, like this example: - -``` -ERROR_LOG_FILE=logs/error.log -ALL_LOG_FILE=logs/all.log -``` - -If you don't set the path, the log is disabled. - -### Access Logging - -Finally, you can enable access logging to record each API request. Here is the format of each log entry: - -``` -:REMOTE_ADDRESS :HTTP_METHOD :URL :HTTP_STATUS :RESPONSE_CONTENT_LENGTH - :RESPONSE_TIME_MS -``` - -To enable access logging, set `ENABLE_ACCESS_LOGGING` to `true`. - -## Development - -### Installation - -Clone code then cd into directory and: - -```bash -npm install -npm run dev -``` - -### Testing - -Testing uses `mocha` and `supertest` to test the endpoints. To run tests: - -```bash -npm run test -``` - -Because `status-service-db` uses database services to manage status, calls are made out to HTTP API endpoints during issuance. Rather than making these calls for every test, and possibly in cases where outgoing HTTP calls aren't ideal, we've mocked the `@digitalcredentials/credential-status-manager-db` package. - -## Contribute - -PRs accepted. - -If editing the Readme, please conform to the -[standard-readme](https://github.com/RichardLitt/standard-readme) specification. - -## License - -[MIT License](LICENSE.md) © 2024 Digital Credentials Consortium. +# status-service-mongo +Implementation of https://www.w3.org/TR/vc-bitstring-status-list/ using Mongo diff --git a/localtunnel.sh b/localtunnel.sh deleted file mode 100755 index bb6ce70..0000000 --- a/localtunnel.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -o allexport -source .env -set +o allexport - -LOCALTUNNEL_COMMAND="lt --port $PORT" - -echo $LOCALTUNNEL_COMMAND - -$LOCALTUNNEL_COMMAND diff --git a/package.json b/package.json deleted file mode 100644 index 3c0c5eb..0000000 --- a/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "@digitalcredentials/status-service-db", - "version": "0.0.0", - "type": "module", - "scripts": { - "start": "node -r dotenv/config server.js", - "dev": "nodemon -r dotenv/config server.js", - "lt": "./localtunnel.sh", - "test": "NODE_OPTIONS=--experimental-vm-modules npx mocha --timeout 10000 -r dotenv/config dotenv_config_path=src/test-fixtures/.env.testing src/app.test.js " - }, - "dependencies": { - "@digitalcredentials/credential-status-manager-db": "digitalcredentials/credential-status-manager-db#initial-implementation", - "cookie-parser": "~1.4.4", - "cors": "^2.8.5", - "debug": "~2.6.9", - "dotenv": "^16.0.3", - "express": "~4.16.1", - "morgan": "~1.9.1", - "winston": "^3.11.0" - }, - "devDependencies": { - "chai": "^4.3.7", - "mocha": "^10.2.0", - "nodemon": "^2.0.21", - "sinon": "^15.2.0", - "supertest": "^6.3.3" - } -} diff --git a/server-dev-only.cert b/server-dev-only.cert deleted file mode 100644 index 2aa4034..0000000 --- a/server-dev-only.cert +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDcjCCAloCCQC5dkGYdTI6MjANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJD -QTEQMA4GA1UECAwHT05UQVJJTzERMA8GA1UEBwwIQU5DQVNURVIxDDAKBgNVBAoM -A0RDQzESMBAGA1UEAwwJbG9jYWxob3N0MSUwIwYJKoZIhvcNAQkBFhZqYy5jaGFy -dHJhbmRAZ21haWwuY29tMB4XDTIzMDEzMTE1NDcwMFoXDTIzMDMwMjE1NDcwMFow -ezELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09OVEFSSU8xETAPBgNVBAcMCEFOQ0FT -VEVSMQwwCgYDVQQKDANEQ0MxEjAQBgNVBAMMCWxvY2FsaG9zdDElMCMGCSqGSIb3 -DQEJARYWamMuY2hhcnRyYW5kQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALswlM6FgU6gPGIFhO0ohyRSJePtDpSLR0wnjyZobVzZjtd8 -5HqbawRfE1zAPAyVmPZ1cUMPKc8owiF1PGdMP/q7Dsk9adUKkEipAHni84fOYgFk -+FxQwV58kjz5zqrCCnYQlLTTk2NtS/wTKl+5k3yXGrYlL41f7vMiWrEFTbvujWAz -Qa0GbcXdckRM3xt8HJb0iMAvOwAMi+gCDPXe3hqwu5AyzaN9tC3JwHLdVBwWtsGv -IlM49SYDnvOmEGxZDTLemhPCypkFWE/A+0VSDjMxs7BgtGfYxcV2grU7lE3bJOxT -fhzCtqtxaHkGSfr0MY+OplEED0foyRchzCyq+8sCAwEAATANBgkqhkiG9w0BAQsF -AAOCAQEAof2Yu6M74yyX8HIampxWE+NIHdRVoYksUYqfjIwEsdWkMzTvcLve5mQn -1PhlYk/5X8aFEMMwMrSZb5EFqV+fOktRymtrUDyYYdopAyct6OWutu4cih47wRdL -qd4dDale4OYJemD7m4VgXa7j0zcJfby8jqZPM+N3iNwLCDGWaGj6CsZDr0BR6xh0 -YyhyZBVCseTLfZGyxmCTHnYg4/cGBoKm/I4CIpXmuLbDMv1n/+xlK9oVn84UvqkY -oydsBrg4gsxHVNMBK8/ftZzDOF5ZVHHNHx0Z37oRE18Al4+1vFzlzPzpKCG9U3Ss -u2NnWz9AjicouE3ikIDINMFctaywEw== ------END CERTIFICATE----- diff --git a/server-dev-only.key b/server-dev-only.key deleted file mode 100644 index e2922d2..0000000 --- a/server-dev-only.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7MJTOhYFOoDxi -BYTtKIckUiXj7Q6Ui0dMJ48maG1c2Y7XfOR6m2sEXxNcwDwMlZj2dXFDDynPKMIh -dTxnTD/6uw7JPWnVCpBIqQB54vOHzmIBZPhcUMFefJI8+c6qwgp2EJS005NjbUv8 -EypfuZN8lxq2JS+NX+7zIlqxBU277o1gM0GtBm3F3XJETN8bfByW9IjALzsADIvo -Agz13t4asLuQMs2jfbQtycBy3VQcFrbBryJTOPUmA57zphBsWQ0y3poTwsqZBVhP -wPtFUg4zMbOwYLRn2MXFdoK1O5RN2yTsU34cwrarcWh5Bkn69DGPjqZRBA9H6MkX -IcwsqvvLAgMBAAECggEBAKhF2d91gHJP9Tggwgf34NSzzEADAJJkSimZfkQGqBlJ -sfDg4vuc7y000tEUNmcRrDoSBUlFPk5t02YEX9J0ZydcNMSPIq5TGrVWx4jKjiXN -T6j1PZavOcVYspWB81jpqkHMUgHkGKDOxfnKuNLonj7oDykicIbkcIe8oE51+BUx -p4AX4yT4rZIzpQi6rRWAiaiWnKA5WZd75JqwvjTNXsrpsiHwHPLUtDJzzknHAK5q -7wPcSDQIgpqop3ScxI+T+x0jZbjGoy6q5R70u5IByYdm6eqYnqT37ly4qDiZwgAh -pVL/KaPLnhQAWYHFcBs9ZhrmuqYMBIJNRzJ9roWxoyECgYEA9J2bT+Z4y+1qQIf4 -hlvluaQ9VXguYysk3JWTx9MfNOQ/Xi/fJTZgY21stGY9uyT8xuhK7QIYalyHRvYH -Wf48Aa85Jfo/Uj59QjCnWdwVNhOxRiR4Gn2piZ0Jkbt5TZqBdRaddqUyJYcPOni9 -eaXaQOIaVgUEvBRIVX5X9+ZTwYkCgYEAw+bK7VBGRWQGZA++UjHjaBRSNbp3vY5m -b/b5LWClLHdQ+GAVFxayRLfzb22zbXQpRQgFDeLZO5NWbQ0hvHf3XjoRtYEuvJQA -niOlXWvJbQpj5A3beRVW9nk8Plw5TyX7ONcA+62qyYLf+o+LoL+OF+NtoeyzoI48 -egdmInDoIbMCgYBPecmVay9CKpAECWlw1fjMmRUoaNTBeaoPVTXfjbOs7p+8DVe1 -8nXcuBfCgRl7bWgHhD+bw7uFCy0UnCkFTznV3kV2FlluckkmMUKeSohFup41SPIQ -wVVNFc2fIMcntJRtI3zjqIajdL844zPEi2NfA1dFLXo9VWWvCU6xh48c6QKBgQCb -VqOswA2osmL67xzsUlDPU+XIYt7V+VezCrSVTeBLvSUAfjfbAg1DvlWTEvrHHOgo -q+5OD4ZP3koW2OXaa4pENmmaciAcOoOu4fcbd//Vrfp9eJuCjPBTKtkhXcG3yBdH -//zwlMorVdLC/RYr1hkXSijB0E6zTnYYEUvknYEETwKBgGQljAqFvIGpWTij8b6k -pwt0xymlOJjRvjTFyQMIQuJLhBG/ydQJhGQ5zvHQVY7S7CFM2QyheP25eqxETTXp -N5BQ854zBX4nqIe7NrY++ldwQg5FQgtR+8ZBiJQwHc5eo74D8acMu54oPbyig+nD -7L+JYn0WFx31GgoeLSmegA8x ------END PRIVATE KEY----- diff --git a/server.js b/server.js deleted file mode 100644 index 32cd356..0000000 --- a/server.js +++ /dev/null @@ -1,27 +0,0 @@ -import { build } from './src/app.js'; -import { getConfig } from './src/config.js'; -import fs from 'fs'; -import http from 'http'; -import https from 'https'; -import logger from './src/utils/logger.js'; - -const run = async () => { - const { port, enableHttpsForDev } = getConfig(); - const app = await build(); - - if (enableHttpsForDev) { - https - .createServer( - { - key: fs.readFileSync('server-dev-only.key'), - cert: fs.readFileSync('server-dev-only.cert') - }, - app - ).listen(port, () => logger.info(`Server running on port ${port} with https`)); - } else { - http - .createServer(app).listen(port, () => logger.info(`Server running on port ${port} with http`)); - } -}; - -run(); diff --git a/src/app.js b/src/app.js deleted file mode 100644 index cf4b0df..0000000 --- a/src/app.js +++ /dev/null @@ -1,123 +0,0 @@ -import express from 'express'; -import cors from 'cors'; -import status from './status.js'; -import accessLogger from './middleware/accessLogger.js'; -import errorHandler from './middleware/errorHandler.js'; -import errorLogger from './middleware/errorLogger.js'; -import invalidPathHandler from './middleware/invalidPathHandler.js'; - -export async function build(opts = {}) { - await status.initializeStatusManager(); - - const app = express(); - - // Add middleware to write http access logs - app.use(accessLogger()); - app.use(express.json()); - app.use(express.urlencoded({ extended: false })); - app.use(cors()); - - app.get('/', function (req, res, next) { - res.json({ message: 'status-service-db server status: ok.' }); - }); - - // Allocate status - app.post('/credentials/status/allocate', - async function (req, res, next) { - try { - const vc = req.body; - if (!vc || !Object.keys(vc).length) { - next({ - message: 'A Verifiable Credential must be provided in the body.', - code: 400 - }); - } - const vcWithStatus = await status.allocateRevocationStatus(vc); - return res.json(vcWithStatus); - } catch (e) { - // We catch the async errors and pass them to the error handler. - if (!e.message) { - e.message = 'Unable to allocate status position.' - } - // Note that if e contains a code property, the following spread of e will - // (correctly) overwrite the 500 - next({ code: 500, ...e }); - } - }); - - // Update status - // The body will look like: - /* - { - "credentialId": "urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1", - "credentialStatus": [{ "type": "BitstringStatusListCredential", "status": "revoked" }] - } - */ - app.post('/credentials/status', - async function (req, res, next) { - try { - const updateRequest = req.body; - if (!updateRequest || !updateRequest.credentialId || !updateRequest.credentialStatus) { - next({ - message: 'A status update request must be provided in the body.', - code: 400 - }); - } - const { credentialId, credentialStatus } = updateRequest; - const statusId = credentialStatus[0].status; - const statusType = credentialStatus[0].type; - - if (statusType !== 'BitstringStatusListCredential') { - next({ - message: 'BitstringStatusListCredential is the only supported status type.', - code: 400 - }); - } - const updateStatusResponse = await status.updateStatus(credentialId, statusId); - return res.status(updateStatusResponse.code).json(updateStatusResponse); - } catch (e) { - // We catch the async errors and pass them to the error handler. - if (!e.message) { - e.message = 'Error updating credential status position.' - } - // Note that if e contains a code property, the following spread of e will - // (correctly) overwrite the 500 - next({ code: 500, ...e }); - } - }); - - // Get credential info - app.get('/credentials/:credentialId', async function (req, res, next) { - const credentialId = req.params.credentialId; - try { - const credentialInfo = await status.getCredentialInfo(credentialId); - return res.status(200).json(credentialInfo); - } catch (error) { - next({ - message: error.message, - code: error.code - }); - } - }); - - // Get status credential - app.get('/:statusCredentialId', async function (req, res, next) { - const statusCredentialId = req.params.statusCredentialId; - try { - const statusCredential = await status.getStatusCredential(statusCredentialId); - return res.status(200).json(statusCredential); - } catch (error) { - next({ - message: error.message, - code: error.code - }); - } - }); - - // Attach the error handling middleware calls, in the order that they should run - app.use(errorLogger); - app.use(errorHandler); - app.use(invalidPathHandler); - - return app; -} diff --git a/src/app.test.js b/src/app.test.js deleted file mode 100644 index 8f70516..0000000 --- a/src/app.test.js +++ /dev/null @@ -1,174 +0,0 @@ -import { expect } from 'chai'; -import sinon from 'sinon'; -import request from 'supertest'; -import { - validCredentialId, - invalidCredentialId, - invalidCredentialIdErrorMessage, - getUnsignedVC, - getUnsignedVCWithStatus, - getValidStatusUpdateBody, - getInvalidStatusUpdateBody -} from './test-fixtures/fixtures.js'; -import status from './status.js'; -import { build } from './app.js'; - -const allocateEndpoint = '/credentials/status/allocate'; -const updateEndpoint = '/credentials/status'; -const emptyStatusManagerStub = {}; - -describe('api', () => { - describe('GET /', () => { - it('GET / => hello', async () => { - await status.initializeStatusManager(emptyStatusManagerStub); - const app = await build(); - - const response = await request(app) - .get('/'); - - expect(response.header['content-type']).to.have.string('json'); - expect(response.status).to.equal(200); - expect(response.body.message).to.equal('status-service-db server status: ok.'); - }); - }); - - describe('GET /unknown/path', () => { - it('unknown endpoint returns 404', async () => { - await status.initializeStatusManager(emptyStatusManagerStub); - const app = await build(); - - const response = await request(app) - .get('/unknown/path'); - - expect(response.status).to.equal(404); - }, 10000); - }); - - describe(`POST ${allocateEndpoint}`, () => { - it('returns 400 if no body', async () => { - await status.initializeStatusManager(emptyStatusManagerStub); - const app = await build(); - - const response = await request(app) - .post(allocateEndpoint); - - expect(response.header['content-type']).to.have.string('json'); - expect(response.status).to.equal(400); - }); - - it('returns updated credential', async () => { - const unsignedVCWithStatus = getUnsignedVCWithStatus(); - const allocateRevocationStatus = sinon.fake.returns(unsignedVCWithStatus); - const statusManagerStub = { allocateRevocationStatus }; - await status.initializeStatusManager(statusManagerStub); - const app = await build(); - - const response = await request(app) - .post(allocateEndpoint) - .send(getUnsignedVC()); - - expect(response.header['content-type']).to.have.string('json'); - expect(response.status).to.equal(200); - expect(response.body).to.eql(unsignedVCWithStatus); - }); - - it('returns unchanged credential when status already set ', async () => { - const allocateRevocationStatus = sinon.fake.returns(getUnsignedVCWithStatus()); - const statusManagerStub = { allocateRevocationStatus }; - await status.initializeStatusManager(statusManagerStub); - const app = await build(); - - const response = await request(app) - .post(allocateEndpoint) - .send(getUnsignedVCWithStatus()); - - expect(response.header['content-type']).to.have.string('json'); - expect(response.status).to.equal(200); - expect(response.body).to.eql(getUnsignedVCWithStatus()); - }); - }); - - describe(`POST ${updateEndpoint}`, () => { - it('returns 400 if no body', async () => { - await status.initializeStatusManager(emptyStatusManagerStub); - const app = await build(); - - const response = await request(app) - .post(updateEndpoint); - - expect(response.header['content-type']).to.have.string('json'); - expect(response.status).to.equal(400); - }); - - it('returns update from revoked credential', async () => { - const revokeCredential = sinon.fake.returns({ - code: 200, - message: 'Credential successfully revoked.' - }); - const statusManagerStub = { revokeCredential }; - await status.initializeStatusManager(statusManagerStub); - const app = await build(); - - const response = await request(app) - .post(updateEndpoint) - .send(getValidStatusUpdateBody(validCredentialId, 'revoked')); - - expect(response.header['content-type']).to.have.string('json'); - expect(response.status).to.equal(200); - expect(response.body.message).to.equal('Credential successfully revoked.'); - }); - - it('returns update from suspended credential', async () => { - const suspendCredential = sinon.fake.returns({ - code: 200, - message: 'Credential successfully suspended.' - }); - const statusManagerStub = { suspendCredential }; - await status.initializeStatusManager(statusManagerStub); - const app = await build(); - - const response = await request(app) - .post(updateEndpoint) - .send(getValidStatusUpdateBody(validCredentialId, 'suspended')); - - expect(response.header['content-type']).to.have.string('json'); - expect(response.status).to.equal(200); - expect(response.body.message).to.equal('Credential successfully suspended.'); - }); - - it('returns update from unsuspended credential', async () => { - const unsuspendCredential = sinon.fake.returns({ - code: 200, - message: 'Credential successfully unsuspended.' - }); - const statusManagerStub = { unsuspendCredential }; - await status.initializeStatusManager(statusManagerStub); - const app = await build(); - - const response = await request(app) - .post(updateEndpoint) - .send(getValidStatusUpdateBody(validCredentialId, 'unsuspended')); - - expect(response.header['content-type']).to.have.string('json'); - expect(response.status).to.equal(200); - expect(response.body.message).to.equal('Credential successfully unsuspended.'); - }); - - it('returns 404 for unknown credential id', async () => { - const missingCredentialError = new Error(invalidCredentialIdErrorMessage); - missingCredentialError.code = 404; - const revokeCredential = sinon.fake.rejects(missingCredentialError); - const statusManagerStub = { revokeCredential }; - await status.initializeStatusManager(statusManagerStub); - const app = await build(); - - const response = await request(app) - .post(updateEndpoint) - .send(getInvalidStatusUpdateBody(invalidCredentialId, 'revoked')); - - expect(response.header['content-type']).to.have.string('json'); - expect(response.status).to.equal(404); - expect(response.body.message).to.contain('Unable to find credential with ID'); - }); - }); -}); diff --git a/src/config.js b/src/config.js deleted file mode 100644 index 4d81552..0000000 --- a/src/config.js +++ /dev/null @@ -1,91 +0,0 @@ - -let CONFIG; -const defaultPort = 4008; -const defaultConsoleLogLevel = 'silly'; -const defaultLogLevel = 'silly'; - -export function setConfig() { - CONFIG = parseConfig(); -} - -function getBooleanValue(value, defaultValue=true) { - value = value?.toLocaleLowerCase(); - if ( - value === 'true' || - value === 't' || - value === 'yes' || - value === 'y' || - value === '1' - ) { - return true; - } else if ( - value === 'false' || - value === 'f' || - value === 'no' || - value === 'n' || - value === '0' - ) { - return false; - } - return defaultValue; -} - -function getGeneralEnvs(env) { - return { - port: env.PORT ? parseInt(env.PORT) : defaultPort, - credStatusService: env.CRED_STATUS_SERVICE, - credStatusDidSeed: env.CRED_STATUS_DID_SEED, - consoleLogLevel: env.CONSOLE_LOG_LEVEL?.toLocaleLowerCase() ?? defaultConsoleLogLevel, - logLevel: env.LOG_LEVEL?.toLocaleLowerCase() ?? defaultLogLevel, - enableAccessLogging: getBooleanValue(env.ENABLE_ACCESS_LOGGING), - enableHttpsForDev: getBooleanValue(env.ENABLE_HTTPS_FOR_DEV), - errorLogFile: env.ERROR_LOG_FILE, - allLogFile: env.ALL_LOG_FILE - }; -} - -function getMongoDbEnvs(env) { - return { - statusCredSiteOrigin: env.STATUS_CRED_SITE_ORIGIN, - credStatusDatabaseUrl: env.CRED_STATUS_DB_URL, - credStatusDatabaseHost: env.CRED_STATUS_DB_HOST, - credStatusDatabasePort: env.CRED_STATUS_DB_PORT, - credStatusDatabaseUsername: env.CRED_STATUS_DB_USER, - credStatusDatabasePassword: env.CRED_STATUS_DB_PASS, - credStatusDatabaseName: env.CRED_STATUS_DB_NAME, - statusCredTableName: env.STATUS_CRED_TABLE_NAME, - userCredTableName: env.USER_CRED_TABLE_NAME, - configTableName: env.CONFIG_TABLE_NAME, - eventTableName: env.EVENT_TABLE_NAME, - credEventTableName: env.CRED_EVENT_TABLE_NAME - }; -} - -function parseConfig() { - const env = process.env; - let serviceSpecificEnvs; - switch (env.CRED_STATUS_SERVICE) { - case 'mongodb': - serviceSpecificEnvs = getMongoDbEnvs(env); - break; - default: - throw new Error(`Encountered unsupported credential status service: ${env.CRED_STATUS_SERVICE}`); - } - const generalEnvs = getGeneralEnvs(env); - const config = Object.freeze({ - ...generalEnvs, - ...serviceSpecificEnvs - }); - return config; -} - -export function getConfig() { - if (!CONFIG) { - setConfig(); - } - return CONFIG; -} - -export function resetConfig() { - CONFIG = null; -} diff --git a/src/middleware/accessLogger.js b/src/middleware/accessLogger.js deleted file mode 100644 index e0557b6..0000000 --- a/src/middleware/accessLogger.js +++ /dev/null @@ -1,28 +0,0 @@ -import morgan from 'morgan'; -import logger from '../utils/logger.js'; -import { getConfig } from '../config.js'; - -const stream = { - write: (message) => logger.http(message) -}; - -const accessLogger = () => { - const { enableAccessLogging } = getConfig(); - // Change the boolean check in skip if - // you want to do things like only log - // when NODE_ENV=development - const skip = () => !enableAccessLogging; - // morgan options. We override 'stream' to instead use winston, - // and override 'skip' to disable logging if disabled in env - const options = { stream, skip }; - // This is the default morgan message format string. - // Switch tokens as you'd like, using either Morgan pre-defined - // tokens or your own custom tokens. - const messageFormatString = ':remote-addr :method :url :status :res[content-length] - :response-time ms'; - return morgan( - messageFormatString, - options - ); -}; - -export default accessLogger; diff --git a/src/middleware/errorHandler.js b/src/middleware/errorHandler.js deleted file mode 100644 index ada2edd..0000000 --- a/src/middleware/errorHandler.js +++ /dev/null @@ -1,15 +0,0 @@ -const errorHandler = (error, request, response, next) => { - // At this point the full details of the error have been - // logged, and now we just need to handle the response. - // We want to return an error that is less verbose - // and more easily understandable, referring to the logs - // for more detail - - const code = error.code | 500; - const message = `An error occurred in status-service-db: ${error.message ?? 'unknown error.'} See the logs for full details. If you are using docker compose, view the logs with 'docker compose logs', and just the status service logs with: 'docker compose logs status-service-db'`; - const errorResponse = {code, message}; - response.header('Content-Type', 'application/json'); - return response.status(error.code).json(errorResponse); -}; - -export default errorHandler; diff --git a/src/middleware/errorLogger.js b/src/middleware/errorLogger.js deleted file mode 100644 index 5fa646d..0000000 --- a/src/middleware/errorLogger.js +++ /dev/null @@ -1,15 +0,0 @@ -import logger from '../utils/logger.js'; - -const errorLogger = (error, request, response, next) => { - const logEntry = {originalError: error}; - logEntry.message = 'An error occurred in status-service-db.'; - const logEntrySummary = `Error for route: ${request.originalUrl} - ${request.method} - IP: ${request.ip} - `; - - // Note that the logEntry here is what Winston calls a 'meta' object. - // Winston simply prints the logEntry to the log as provided - JSON in this case. - logger.error(logEntrySummary, logEntry); - - next(error); // done logging, so call the next middleware that deals with errors -}; - -export default errorLogger; diff --git a/src/middleware/invalidPathHandler.js b/src/middleware/invalidPathHandler.js deleted file mode 100644 index 56e482f..0000000 --- a/src/middleware/invalidPathHandler.js +++ /dev/null @@ -1,9 +0,0 @@ -import logger from '../utils/logger.js'; - -// Fallback middleware for undefined paths -const invalidPathHandler = (req, res) => { - res.status(404).send({code: 404, message: `Route Not Found in status-service-db: ${req.originalUrl}`}); - logger.error(`404 || ${res.statusMessage} - ${req.originalUrl} - ${req.method} - ${req.ip}`); -}; - -export default invalidPathHandler; diff --git a/src/status.js b/src/status.js deleted file mode 100644 index 5431f41..0000000 --- a/src/status.js +++ /dev/null @@ -1,125 +0,0 @@ -import { createStatusManager } from '@digitalcredentials/credential-status-manager-db'; -import { getConfig } from './config.js'; - -const { - credStatusService, - statusCredSiteOrigin, - credStatusDatabaseUrl, - credStatusDatabaseHost, - credStatusDatabasePort, - credStatusDatabaseUsername, - credStatusDatabasePassword, - credStatusDatabaseName, - statusCredTableName, - userCredTableName, - configTableName, - eventTableName, - credEventTableName, - credStatusDidSeed -} = getConfig(); - -let STATUS_LIST_MANAGER; - -async function createMongoDbStatusManager() { - return createStatusManager({ - statusCredentialSiteOrigin: statusCredSiteOrigin, - databaseService: credStatusService, - databaseUrl: credStatusDatabaseUrl, - databaseHost: credStatusDatabaseHost, - databasePort: credStatusDatabasePort, - databaseUsername: credStatusDatabaseUsername, - databasePassword: credStatusDatabasePassword, - databaseName: credStatusDatabaseName, - statusCredentialTableName: statusCredTableName, - userCredentialTableName: userCredTableName, - configTableName, - eventTableName, - credentialEventTableName: credEventTableName, - didMethod: 'key', - didSeed: credStatusDidSeed, - // This is the already the default value, - // but setting here to be explicit - autoDeployDatabase: true, - // This is the already the default value, - // but setting here to be explicit - signStatusCredential: true, - // This is the already the default value, - // but setting here to be explicit - signUserCredential: false - }); -} - -/* we allow passing in a status manager, for testing */ -async function initializeStatusManager(statusManager) { - if (statusManager) { - STATUS_LIST_MANAGER = statusManager; - return; - } else if (STATUS_LIST_MANAGER) { - return; - } - - switch (credStatusService) { - case 'mongodb': - STATUS_LIST_MANAGER = await createMongoDbStatusManager(); - break; - default: - throw new Error(`Encountered unsupported credential status service: ${credStatusService}`); - } -} - -async function getStatusManager() { - await initializeStatusManager(); - return STATUS_LIST_MANAGER; -} - -async function allocateRevocationStatus(verifiableCredential) { - const statusManager = await getStatusManager(); - const result = verifiableCredential.credentialStatus ? - verifiableCredential : - await statusManager.allocateRevocationStatus(verifiableCredential); - return result; -} - -async function updateStatus(credentialId, credentialStatus) { - const statusManager = await getStatusManager(); - try { - switch (credentialStatus) { - case 'revoked': - await statusManager.revokeCredential(credentialId); - return { code: 200, message: 'Credential successfully revoked.' }; - case 'suspended': - await statusManager.suspendCredential(credentialId); - return { code: 200, message: 'Credential successfully suspended.' }; - case 'unsuspended': - await statusManager.unsuspendCredential(credentialId); - return { code: 200, message: 'Credential successfully unsuspended.' }; - default: - return { code: 400, message: `Unsupported credential status: "${credentialStatus}"` }; - } - } catch (error) { - return { - code: error.code ?? 500, - message: error.message ?? - `Unable to apply status "${credentialStatus}" to credential with ID "${credentialId}".` - }; - } -} - -async function getCredentialInfo(credentialId) { - const statusManager = await getStatusManager(); - return statusManager.getCredentialInfo(credentialId); -} - -async function getStatusCredential(statusCredentialId) { - const statusManager = await getStatusManager(); - return await statusManager.getStatusCredential(statusCredentialId); -} - -export default { - initializeStatusManager, - getStatusManager, - allocateRevocationStatus, - updateStatus, - getCredentialInfo, - getStatusCredential -}; diff --git a/src/test-fixtures/.env.testing b/src/test-fixtures/.env.testing deleted file mode 100644 index 1a06f8c..0000000 --- a/src/test-fixtures/.env.testing +++ /dev/null @@ -1,22 +0,0 @@ -# PORT=4008 #default port is 4008 - -# the CRED_STATUS_* values are used to instantiate the status list manager -CRED_STATUS_SERVICE=mongodb -STATUS_CRED_SITE_ORIGIN=https://credentials.example.edu -CRED_STATUS_DB_URL=mongodb+srv://user:pass@domain.mongodb.net?retryWrites=false -CRED_STATUS_DB_HOST=domain.mongodb.net # ignored if CRED_STATUS_DB_URL is configured -CRED_STATUS_DB_PORT=27017 # ignored if CRED_STATUS_DB_URL is configured -CRED_STATUS_DB_USER=testuser # ignored if CRED_STATUS_DB_URL is configured -CRED_STATUS_DB_PASS=testpass # ignored if CRED_STATUS_DB_URL is configured -CRED_STATUS_DB_NAME= -STATUS_CRED_TABLE_NAME= -USER_CRED_TABLE_NAME= -CONFIG_TABLE_NAME= -EVENT_TABLE_NAME= -CRED_EVENT_TABLE_NAME= -CRED_STATUS_DID_SEED=z1AackbUm8U69ohKnihoRRFkXcXJd4Ra1PkAboQ2ZRy1ngB - -ERROR_LOG_FILE=logs/error.log -ALL_LOG_FILE=logs/all.log -CONSOLE_LOG_LEVEL=silly # default is silly, i.e. log everything - see the README for allowed levels -LOG_LEVEL=silly # default is silly diff --git a/src/test-fixtures/fixtures.js b/src/test-fixtures/fixtures.js deleted file mode 100644 index 859b9a2..0000000 --- a/src/test-fixtures/fixtures.js +++ /dev/null @@ -1,61 +0,0 @@ -import testVC from './testVC.js'; - -const validCredentialId = 'urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1'; -const invalidCredentialId = 'kj09ij'; -const invalidCredentialIdErrorMessage = 'An error occurred in status-service-db: ' + - `Unable to find credential with ID ${invalidCredentialId}`; - -const credentialStatus = [ - { - "id": "https://digitalcredentials.github.io/credential-status-jc-test/XA5AAK1PV4#2", - "type": "BitstringStatusListEntry", - "statusPurpose": "revocation", - "statusListIndex": 2, - "statusListCredential": "https://digitalcredentials.github.io/credential-status-jc-test/XA5AAK1PV4" - }, - { - "id": "https://digitalcredentials.github.io/credential-status-jc-test/DKSPRCX9WB#5", - "type": "BitstringStatusListEntry", - "statusPurpose": "suspension", - "statusListIndex": 5, - "statusListCredential": "https://digitalcredentials.github.io/credential-status-jc-test/DKSPRCX9WB" - } -]; - -const statusUpdateBody = { - "credentialId": "urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1", - "credentialStatus": [{ "type": "BitstringStatusListCredential", "status": "revoked" }] -}; - -const getUnsignedVC = () => JSON.parse(JSON.stringify(testVC)); - -const getValidStatusUpdateBody = (credentialId, status) => { - statusUpdateBody.credentialId = credentialId; - statusUpdateBody.credentialStatus[0].status = status; - return JSON.parse(JSON.stringify(statusUpdateBody)); -}; - -const getInvalidStatusUpdateBody = (credentialId, status) => { - const updateBody = getValidStatusUpdateBody(credentialId, status); - updateBody.credentialId = credentialId; - return updateBody; -}; - -const getCredentialStatus = () => JSON.parse(JSON.stringify(credentialStatus)); - -const getUnsignedVCWithStatus = () => { - const unsignedVCWithStatus = getUnsignedVC(); - unsignedVCWithStatus.credentialStatus = getCredentialStatus(); - return unsignedVCWithStatus; -}; - -export { - validCredentialId, - invalidCredentialId, - invalidCredentialIdErrorMessage, - getUnsignedVC, - getCredentialStatus, - getUnsignedVCWithStatus, - getValidStatusUpdateBody, - getInvalidStatusUpdateBody -}; diff --git a/src/test-fixtures/testVC.js b/src/test-fixtures/testVC.js deleted file mode 100644 index add91e2..0000000 --- a/src/test-fixtures/testVC.js +++ /dev/null @@ -1,37 +0,0 @@ -export default { - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://purl.imsglobal.org/spec/ob/v3p0/context.json" - ], - "id": "urn:uuid:951b475e-b795-43bc-ba8f-a2d01efd2eb1", - "type": [ - "VerifiableCredential", - "OpenBadgeCredential" - ], - "issuer": { - "id": "did:key:z6MkhVTX9BF3NGYX6cc7jWpbNnR7cAjH8LUffabZP8Qu4ysC", - "type": "Profile", - "name": "University of Wonderful", - "description": "The most wonderful university", - "url": "https://wonderful.edu/", - "image": { - "id": "https://user-images.githubusercontent.com/947005/133544904-29d6139d-2e7b-4fe2-b6e9-7d1022bb6a45.png", - "type": "Image" - } - }, - "validFrom": "2020-01-01T00:00:00Z", - "name": "A Simply Wonderful Course", - "credentialSubject": { - "type": "AchievementSubject", - "achievement": { - "id": "http://wonderful.wonderful", - "type": "Achievement", - "criteria": { - "narrative": "Completion of the Wonderful Course - well done you!" - }, - "description": "Wonderful.", - "name": "Introduction to Wonderfullness" - } - } - } - \ No newline at end of file diff --git a/src/utils/logger.js b/src/utils/logger.js deleted file mode 100644 index 3ee63df..0000000 --- a/src/utils/logger.js +++ /dev/null @@ -1,77 +0,0 @@ -import winston from 'winston'; -import { getConfig } from '../config.js'; - -const { errorLogFile, allLogFile, logLevel, consoleLogLevel } = getConfig(); -/* -These are the default npm logging levels -that Winston uses, but we include them explicitly -here in case you want to change them -*/ -const levels = { - error: 0, - warn: 1, - info: 2, - http: 3, - verbose: 4, - debug: 5, - silly: 6 -}; - -// Set severity using LOG_LEVEL from env. -// If LOG_LEVEL is not set then set -// it using NODE_ENV from env, where: -// development: silly, i.e, log everything -// production: warn and error -const level = () => { - if (logLevel) { - return logLevel; - } else { - const env = process.env.NODE_ENV ?? 'development'; - const isDevelopment = env === 'development'; - return isDevelopment ? 'silly' : 'warn'; - } -}; - -const format = winston.format.combine( - winston.format.timestamp(), - winston.format.json() -); - -/* -Here we output as defined in the env -*/ -const transports = []; - -if (consoleLogLevel.toLowerCase() !== 'none') { - transports.push( - new winston.transports.Console({ - level: consoleLogLevel - }) - ); -} - -if (errorLogFile) { - transports.push( - new winston.transports.File({ - filename: errorLogFile, - level: 'error', - }) - ); -} - -if (allLogFile) { - transports.push( - new winston.transports.File({ - filename: allLogFile - }) - ); -} - -const logger = winston.createLogger({ - level: level(), - levels, - format, - transports, -}); - -export default logger;