Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gRPC bindings #1264

Merged
merged 37 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
89b735c
First commit: project structure and health_check service
UMR1352 Nov 20, 2023
9b0a2e5
need changes on another pc
UMR1352 Nov 22, 2023
9cebdb2
Credential revocation status check + basic test
UMR1352 Nov 24, 2023
cbf2736
Better (?) error handling
UMR1352 Nov 27, 2023
b62be12
Slightly more structured error handling
UMR1352 Nov 28, 2023
51adf63
Dockerfile
UMR1352 Nov 28, 2023
0077272
Merge branch 'main' into grpc-bindings
UMR1352 Jan 18, 2024
a8caa78
sd-jwt validation
UMR1352 Jan 19, 2024
620e5d8
KbSdJwt validation and fmt
UMR1352 Jan 23, 2024
ac9f73a
SD-JWT validation service integration test
UMR1352 Jan 23, 2024
7b1dac2
add tracing
UMR1352 Jan 24, 2024
773942f
readme
UMR1352 Jan 25, 2024
ba1afe1
Rename README to README.md
UMR1352 Jan 25, 2024
d0eb861
Use keymaterial from stronghold, jwt creation service
UMR1352 Jan 31, 2024
eae3e6b
Merge branch 'main' into grpc-bindings
UMR1352 Feb 26, 2024
a60c1ce
add dockerhub publish action
eike-hass Mar 8, 2024
05c706f
Merge branch 'main' into grpc-bindings
eike-hass Mar 8, 2024
325a621
VC validation service and integration test
UMR1352 Mar 11, 2024
470f1f4
update docker publish workflow
eike-hass Mar 14, 2024
e0ede7f
move grpc dockerfile
eike-hass Mar 14, 2024
1ec95ef
add grpc build as workflow
eike-hass Mar 14, 2024
ea57380
Merge branch 'main' into grpc-bindings
eike-hass Mar 14, 2024
3d58d74
fmt
eike-hass Mar 14, 2024
c939d03
did-creation
UMR1352 Mar 14, 2024
0980069
update readme
UMR1352 Mar 14, 2024
1ea3fb2
Add domain linkage grpc (#1337)
wulfraem Mar 22, 2024
bda1283
Feat/grpc status list 2021 (#1338)
UMR1352 Mar 22, 2024
ccb5ee0
minor readme rework
eike-hass Mar 25, 2024
d747f22
minor readme rework
eike-hass Mar 25, 2024
61add39
add readme and descripion for dockerhub
eike-hass Mar 25, 2024
88f3d91
proto files comments
UMR1352 Mar 25, 2024
4ee4520
Merge "main" into "grpc-bindings"
UMR1352 Mar 25, 2024
cd8adbc
add license header
UMR1352 Mar 25, 2024
e700dff
fix typo
UMR1352 Mar 26, 2024
4cc6dc2
use vendored ssl
eike-hass Mar 26, 2024
37113f9
fix formatting
eike-hass Mar 26, 2024
63045eb
Merge branch master into grpc-bindings
UMR1352 Mar 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
target/
bindings/wasm/
bindings/grpc/target/
41 changes: 41 additions & 0 deletions .github/workflows/build-and-test-grpc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Build and run grpc tests

on:
push:
branches:
- main
pull_request:
types: [ opened, synchronize, reopened, ready_for_review ]
branches:
- main
- 'epic/**'
- 'support/**'
paths:
- '.github/workflows/build-and-test.yml'
- '.github/actions/**'
- '**.rs'
- '**.toml'
- 'bindings/grpc/**'

jobs:
check-for-run-condition:
runs-on: ubuntu-latest
outputs:
should-run: ${{ !github.event.pull_request || github.event.pull_request.draft == false }}
steps:
- run: |
# this run step does nothing, but is needed to get the job output

build-and-test:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4

- name: Build Docker image
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
with:
context: .
file: bindings/grpc/Dockerfile
push: false
labels: iotaledger/identity-grpc:latest
42 changes: 42 additions & 0 deletions .github/workflows/grpc-publish-to-dockerhub.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: gRPC publish to dockerhub

on:
workflow_dispatch:
inputs:
tag:
description: 'Tag to publish under, defaults to latest'
required: false
default: latest
branch:
description: 'Branch to run publish from'
required: true
dry-run:
description: 'Run in dry-run mode'
type: boolean
required: false
default: true

jobs:
push_to_registry:
environment: release
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}

- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.IOTALEDGER_DOCKER_USERNAME }}
password: ${{ secrets.IOTALEDGER_DOCKER_PASSWORD }}

- name: Build and push Docker image
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
with:
context: .
file: bindings/grpc/Dockerfile
push: ${{ !inputs.dry-run }}
labels: iotaledger/identity-grpc:${{ inputs.tag }}
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ members = [
"examples",
]

exclude = ["bindings/wasm"]
exclude = ["bindings/wasm", "bindings/grpc"]

[workspace.dependencies]
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
Expand Down
42 changes: 42 additions & 0 deletions bindings/grpc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[package]
name = "identity-grpc"
version = "0.1.0"
authors = ["IOTA Stiftung"]
edition = "2021"
homepage = "https://www.iota.org"
license = "Apache-2.0"
repository = "https://github.com/iotaledger/identity.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
path = "src/lib.rs"

[[bin]]
name = "identity-grpc"
path = "src/main.rs"

[dependencies]
anyhow = "1.0.75"
futures = { version = "0.3" }
identity_eddsa_verifier = { path = "../../identity_eddsa_verifier" }
identity_iota = { path = "../../identity_iota", features = ["resolver", "sd-jwt", "domain-linkage", "domain-linkage-fetch", "status-list-2021"] }
identity_stronghold = { path = "../../identity_stronghold", features = ["send-sync-storage"] }
iota-sdk = { version = "1.1.2", features = ["stronghold"] }
prost = "0.12"
rand = "0.8.5"
serde = { version = "1.0.193", features = ["derive", "alloc"] }
serde_json = { version = "1.0.108", features = ["alloc"] }
thiserror = "1.0.50"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
tokio-stream = { version = "0.1.14", features = ["net"] }
tonic = "0.10"
tracing = { version = "0.1.40", features = ["async-await"] }
tracing-subscriber = "0.3.18"
url = { version = "2.5", default-features = false }

[dev-dependencies]
identity_storage = { path = "../../identity_storage", features = ["memstore"] }

[build-dependencies]
tonic-build = "0.10"
20 changes: 20 additions & 0 deletions bindings/grpc/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM rust:bookworm as builder

# install protobuf
RUN apt-get update && apt-get install -y protobuf-compiler libprotobuf-dev musl-tools

COPY . /usr/src/app/
WORKDIR /usr/src/app/bindings/grpc
RUN rustup target add x86_64-unknown-linux-musl
RUN cargo build --target x86_64-unknown-linux-musl --release --bin identity-grpc

FROM gcr.io/distroless/static-debian11 as runner

# get binary
COPY --from=builder /usr/src/app/bindings/grpc/target/x86_64-unknown-linux-musl/release/identity-grpc /

# set run env
EXPOSE 50051

# run it
CMD ["/identity-grpc"]
129 changes: 129 additions & 0 deletions bindings/grpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Identity.rs gRPC Bindings
This project provides the functionalities of [Identity.rs](https://github.com/iotaledger/identity.rs) in a language-agnostic way through a [gRPC](https://grpc.io) server.

The server can easily be run with docker using [this dockerfile](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/Dockerfile).

## Build
Run `docker build -f bindings/grpc/Dockerfile -t iotaleger/identity-grpc .` from the project root.

### Dockerimage env variables and volume binds
The provided docker image requires the following variables to be set in order to properly work:
- `API_ENDPOINT`: IOTA's node address.
- `STRONGHOLD_PWD`: Stronghold password.
- `SNAPSHOT_PATH`: Stronghold's snapshot location.

Make sure to provide a valid stronghold snapshot at the provided `SNAPSHOT_PATH` prefilled with all the needed key material.

### Available services
| Service description | Service Id | Proto File |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------|
| Credential Revocation Checking | `credentials/CredentialRevocation.check` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) |
| SD-JWT Validation | `sd_jwt/Verification.verify` | [sd_jwt.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/sd_jwt.proto) |
| Credential JWT creation | `credentials/Jwt.create` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) |
| Credential JWT validation | `credentials/VcValidation.validate` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) |
| DID Document Creation | `document/DocumentService.create` | [document.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/document.proto) |
| Domain Linkage - validate domain, let server fetch did-configuration | `domain_linkage/DomainLinkage.validate_domain` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) |
| Domain Linkage - validate domain, pass did-configuration to service | `domain_linkage/DomainLinkage.validate_domain_against_did_configuration` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) |
| Domain Linkage - validate endpoints in DID, let server fetch did-configuration | `domain_linkage/DomainLinkage.validate_did` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) |
| Domain Linkage - validate endpoints in DID, pass did-configuration to service | `domain_linkage/DomainLinkage.validate_did_against_did_configurations` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) |
| `StatusList2021Credential` creation | `status_list_2021/StatusList2021Svc.create` | [status_list_2021.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/status_list_2021.proto) |
| `StatusList2021Credential` update | `status_list_2021/StatusList2021Svc.update` | [status_list_2021.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/status_list_2021.proto) |

## Testing
### Domain Linkage
Following is a description about how to manually test the domain linkage service. The steps for the other services might vary a bit.

#### Http server
If you want to test domain linkage, you need a server, that's reachable via HTTPS. If you already have one, ignore the server setup steps here and just make sure your server provides the `did-configuration.json` file as described here.

- create test server folder with did configuration in it, e.g. (you can also use the template in `./tooling/domain-linkage-test-server`)
```raw
test-server/
└── .well-known
└── did-configuration.json
```

`did-configuration` looks like this for now:

```json
{
"@context": "https://identity.foundation/.well-known/did-configuration/v1",
"linked_dids": [
"add your domain linkage credential here"
]
}
```
- start a server, that will serve this folder, e.g. with a "http-server" from NodeJs : `http-server ./test-server/`, in this example the server should now be running on local port 8080
- now tunnel your server's port (here 8080) to a public domain with https, e.g. with ngrok:
`ngrok http http://127.0.0.1:8080`
the output should now have a line like
`Forwarding https://0d40-2003-d3-2710-e200-485f-e8bb-7431-79a7.ngrok-free.app -> http://127.0.0.1:8080`
check that the https url is reachable, this will be used in the next step. you can also start ngrok with a static domain, that you do not have to update credentials after each http server restart
- for convenience, you can find a script to start the HTTP server, that you can adjust in `tooling/start-http-server.sh`, don't forget to insert your static domain or to remove the `--domain` parameter

#### Domain linkage credential
- copy this public url and insert it into the advanced test 6 (the one for domain linkage) as domain 1, e.g. `let domain_1: Url = Url::parse("https://0d40-2003-d3-2710-e200-485f-e8bb-7431-79a7.ngrok-free.app")?;`
- run the example with `cargo run --release --example 6_domain_linkage`

#### GRPC server
- grab the configuration resource from the log and replace the contents of your `did-configuration.json` with it
- you now have a publicly reachable (sub)domain, that serves a `did-configuration` file containing a credential pointing to your DID
- to verify this, run the server via Docker or with the following command, remember to replace the placeholders ;) `API_ENDPOINT=replace_me STRONGHOLD_PWD=replace_me SNAPSHOT_PATH=replace_me cargo run --release`, arguments can be taken from examples, e.g. after running a `6_domain_linkage.rs`, that also logs snapshot path passed to secret manager (`let snapshot_path = random_stronghold_path(); dbg!(&snapshot_path.to_str());`), for example
- API_ENDPOINT: `"http://localhost"`
- STRONGHOLD_PWD: `"secure_password"`
- SNAPSHOT_PATH: `"/var/folders/41/s1sm86jx0xl4x435t81j81440000gn/T/test_strongholds/8o2Nyiv5ENBi7Ik3dEDq9gNzSrqeUdqi.stronghold"`
- for convenience, you can find a script to start the GRPC server, that you can adjust in `tooling/start-rpc-server.sh`, don't forget to insert the env variables as described above

#### Calling the endpoints
- call the `validate_domain` endpoint with your domain, e.g with:

```json
{
"domain": "https://0d40-2003-d3-2710-e200-485f-e8bb-7431-79a7.ngrok-free.app"
}
```

you should now receive a response like this:

```json
{
"linked_dids": [
{
"document": "... (compact JWT domain linkage credential)",
"status": "ok"
}
]
}
```

- to call the `validate_did` endpoint, you need a DID to check, you can find a testable in you domain linkage credential. for this just decode it (e.g. on jwt.io) and get the `iss` value, then you can submit as "did" like following

```json
{
"did": "did:iota:snd:0x967bf8f0c7487f61378611b6a1c6a59cb99e65b839681ee70be691b09a024ab9"
}
```

you should not receive a response like this:

```json
{
"service": [
{
"service_endpoint": [
{
"valid": true,
"document": "eyJraWQiOiJkaWQ6aW90YTpzbmQ6MHg5NjdiZjhmMGM3NDg3ZjYxMzc4NjExYjZhMWM2YTU5Y2I5OWU2NWI4Mzk2ODFlZTcwYmU2OTFiMDlhMDI0YWI5IzA3QjVWRkxBa0FabkRhaC1OTnYwYUN3TzJ5ZnRzX09ZZ0YzNFNudUloMlUiLCJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9.eyJleHAiOjE3NDE2NzgyNzUsImlzcyI6ImRpZDppb3RhOnNuZDoweDk2N2JmOGYwYzc0ODdmNjEzNzg2MTFiNmExYzZhNTljYjk5ZTY1YjgzOTY4MWVlNzBiZTY5MWIwOWEwMjRhYjkiLCJuYmYiOjE3MTAxNDIyNzUsInN1YiI6ImRpZDppb3RhOnNuZDoweDk2N2JmOGYwYzc0ODdmNjEzNzg2MTFiNmExYzZhNTljYjk5ZTY1YjgzOTY4MWVlNzBiZTY5MWIwOWEwMjRhYjkiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vaWRlbnRpdHkuZm91bmRhdGlvbi8ud2VsbC1rbm93bi9kaWQtY29uZmlndXJhdGlvbi92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiRG9tYWluTGlua2FnZUNyZWRlbnRpYWwiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsib3JpZ2luIjoiaHR0cHM6Ly9ob3QtYnVsbGRvZy1wcm9mb3VuZC5uZ3Jvay1mcmVlLmFwcC8ifX19.69e7T0DbRw9Kz7eEQ96P9E5HWbEo5F1fLuMjyQN6_Oa1lwBdbfj0wLlhS1j_d8AuNmvu60lMdLVixjMZJLQ5AA"
},
{
"valid": false,
"error": "domain linkage error: error sending request for url (https://bar.example.com/.well-known/did-configuration.json): error trying to connect: dns error: failed to lookup address information: nodename nor servname provided, or not known"
}
],
"id": "did:iota:snd:0x967bf8f0c7487f61378611b6a1c6a59cb99e65b839681ee70be691b09a024ab9"
}
]
}
```

Which tells us that it found a DID document with one matching service with a serviceEndpoint, that contains two domains. Out of these domains one links back to the given DID, the other domain could not be resolved.
11 changes: 11 additions & 0 deletions bindings/grpc/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
let proto_files = std::fs::read_dir("./proto")?
.filter_map(|entry| entry.ok().map(|e| e.path()))
.filter(|path| path.extension().and_then(|ext| ext.to_str()) == Some("proto"));

for proto in proto_files {
tonic_build::compile_protos(proto)?;
}

Ok(())
}
50 changes: 50 additions & 0 deletions bindings/grpc/proto/credentials.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
syntax = "proto3";
package credentials;

// -- CREDENTIALS REVOCATION ---------------------------------------------

enum RevocationStatus {
REVOKED = 0;
SUSPENDED = 1;
VALID = 2;
}

message RevocationCheckRequest {
string type = 1;
string url = 2;
map<string, string> properties = 3;
}

message RevocationCheckResponse {
RevocationStatus status = 1;
}

service CredentialRevocation {
rpc check(RevocationCheckRequest) returns (RevocationCheckResponse);
}

message JwtCreationRequest {
string credential_json = 1;
string issuer_fragment = 2;
}

message JwtCreationResponse {
string jwt = 1;
}

service Jwt {
rpc create(JwtCreationRequest) returns (JwtCreationResponse);
}

message VcValidationRequest {
string credential_jwt = 1;
optional string status_list_credential_json = 2;
}

message VcValidationResponse {
string credential_json = 1;
}

service VcValidation {
rpc validate(VcValidationRequest) returns (VcValidationResponse);
}
16 changes: 16 additions & 0 deletions bindings/grpc/proto/document.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
syntax = "proto3";
package document;

message CreateDIDRequest {
string bech32_address = 1;
}

message CreateDIDResponse {
string document_json = 1;
string fragment = 2;
string did = 3;
}

service DocumentService {
rpc create(CreateDIDRequest) returns (CreateDIDResponse);
}
Loading