Skip to content

Commit

Permalink
Merge branch 'beta' into feat/generate-openapi
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-mader committed Dec 16, 2024
2 parents 8061154 + 65c9653 commit 27856c4
Show file tree
Hide file tree
Showing 31 changed files with 397 additions and 109 deletions.
14 changes: 8 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ rust-version = "1.76.0"

[workspace.dependencies]
did_manager = { git = "https://[email protected]/impierce/did-manager.git", tag = "v1.0.0-beta.3" }
siopv2 = { git = "https://[email protected]/impierce/openid4vc.git", rev = "12fed14" }
oid4vci = { git = "https://[email protected]/impierce/openid4vc.git", rev = "12fed14" }
oid4vc-core = { git = "https://[email protected]/impierce/openid4vc.git", rev = "12fed14" }
oid4vc-manager = { git = "https://[email protected]/impierce/openid4vc.git", rev = "12fed14" }
oid4vp = { git = "https://[email protected]/impierce/openid4vc.git", rev = "12fed14" }
siopv2 = { git = "https://[email protected]/impierce/openid4vc.git", rev = "7be5b72" }
oid4vci = { git = "https://[email protected]/impierce/openid4vc.git", rev = "7be5b72" }
oid4vc-core = { git = "https://[email protected]/impierce/openid4vc.git", rev = "7be5b72" }
oid4vc-manager = { git = "https://[email protected]/impierce/openid4vc.git", rev = "7be5b72" }
oid4vp = { git = "https://[email protected]/impierce/openid4vc.git", rev = "7be5b72" }

async-trait = "0.1"
axum = { version = "0.7", features = ["tracing"] }
base64 = "0.22"
chrono = { version = "0.4", features = ["serde"] }
cqrs-es = "0.4.2"
futures = "0.3"
identity_core = "1.3"
Expand Down
67 changes: 64 additions & 3 deletions agent_api_rest/postman/ssi-agent.postman_collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"offerId\":\"{{OFFER_ID}}\",\n \"credentialConfigurationId\": \"openbadge_credential\",\n \"credential\": {\n \"credentialSubject\": {\n \"type\": [ \"AchievementSubject\" ],\n \"achievement\": {\n \"id\": \"https://example.com/achievements/21st-century-skills/teamwork\",\n \"type\": \"Achievement\",\n \"criteria\": {\n \"narrative\": \"Team members are nominated for this badge by their peers and recognized upon review by Example Corp management.\"\n },\n \"description\": \"This badge recognizes the development of the capacity to collaborate within a group environment.\",\n \"name\": \"Teamwork\"\n }\n }\n }\n}",
"raw": "{\n \"offerId\":\"{{OFFER_ID}}\",\n \"credentialConfigurationId\": \"openbadge_credential\",\n \"credential\": {\n \"credentialSubject\": {\n \"type\": [ \"AchievementSubject\" ],\n \"achievement\": {\n \"id\": \"https://example.com/achievements/21st-century-skills/teamwork\",\n \"type\": \"Achievement\",\n \"criteria\": {\n \"narrative\": \"Team members are nominated for this badge by their peers and recognized upon review by Example Corp management.\"\n },\n \"description\": \"This badge recognizes the development of the capacity to collaborate within a group environment.\",\n \"name\": \"Teamwork\"\n }\n }\n },\n \"expiresAt\": \"2028-04-12T09:15:23Z\"\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -97,7 +97,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"offerId\":\"{{OFFER_ID}}\",\n \"credentialConfigurationId\": \"w3c_vc_credential\",\n \"credential\": {\n \"credentialSubject\": {\n \"first_name\": \"Ferris\",\n \"last_name\": \"Crabman\",\n \"dob\": \"1982-01-01\"\n }\n }\n}",
"raw": "{\n \"offerId\":\"{{OFFER_ID}}\",\n \"credentialConfigurationId\": \"w3c_vc_credential\",\n \"credential\": {\n \"credentialSubject\": {\n \"first_name\": \"Ferris\",\n \"last_name\": \"Crabman\",\n \"dob\": \"1982-01-01\"\n }\n },\n \"expiresAt\": \"never\"\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -151,7 +151,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"offerId\":\"{{OFFER_ID}}\",\n \"credentialConfigurationId\": \"w3c_vc_credential\",\n \"credential\": {\n \"credentialSubject\": {\n \"id\": \"https://ecommerce.impierce.com/\",\n \"image\": \"https://static.wikia.nocookie.net/fictionalcompanies/images/c/c2/ACME_Corporation.png\",\n \"name\": \"VirtualVendors\",\n \"certificaat\": {\n \"type\": \"ACMECorpCredential\",\n \"certificeringsDatum\": \"2024-06-26\",\n \"geldigheidsPeriode\": \"1 jaar\",\n \"garanties\": [\n \"Het bedrijf is echt en bereikbaar.\",\n \"Voldoet aan de Thuiswinkel Algemene Voorwaarden.\",\n \"14 dagen bedenktijd.\",\n \"Veilige betaalmethoden.\",\n \"Duidelijke product/servicebeschrijvingen.\",\n \"Transparant bestelproces.\",\n \"Duidelijke prijzen.\",\n \"Veilige betaalomgeving.\",\n \"Veilige omgang met persoonlijke gegevens.\",\n \"Effectieve klachtenafhandeling en onafhankelijke geschillenbemiddeling.\"\n ]\n }\n }\n }\n}",
"raw": "{\n \"offerId\":\"{{OFFER_ID}}\",\n \"credentialConfigurationId\": \"w3c_vc_credential\",\n \"credential\": {\n \"id\": \"https://acme.example.org/1a2b3c4d5e6f\",\n \"credentialSubject\": {\n \"id\": \"https://ecommerce.impierce.com/\",\n \"image\": \"https://static.wikia.nocookie.net/fictionalcompanies/images/c/c2/ACME_Corporation.png\",\n \"name\": \"VirtualVendors\",\n \"certificaat\": {\n \"type\": \"ACMECorpCredential\",\n \"certificeringsDatum\": \"2024-06-26\",\n \"geldigheidsPeriode\": \"1 jaar\",\n \"garanties\": [\n \"Het bedrijf is echt en bereikbaar.\",\n \"Voldoet aan de Thuiswinkel Algemene Voorwaarden.\",\n \"14 dagen bedenktijd.\",\n \"Veilige betaalmethoden.\",\n \"Duidelijke product/servicebeschrijvingen.\",\n \"Transparant bestelproces.\",\n \"Duidelijke prijzen.\",\n \"Veilige betaalomgeving.\",\n \"Veilige omgang met persoonlijke gegevens.\",\n \"Effectieve klachtenafhandeling en onafhankelijke geschillenbemiddeling.\"\n ]\n }\n }\n },\n \"expiresAt\": \"2035-12-31T23:59:59Z\"\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -958,6 +958,45 @@
},
"response": []
},
{
"name": "Add signed Holder Credential",
"event": [
{
"listen": "test",
"script": {
"exec": [
""
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"credential\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2dFODROQ01wTWVBeDlqSzljZjVXNEc4Z2NaOXh1d0p2RzFlN3dOazhLQ2d0I3o2TWtnRTg0TkNNcE1lQXg5aks5Y2Y1VzRHOGdjWjl4dXdKdkcxZTd3Tms4S0NndCJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtnRTg0TkNNcE1lQXg5aks5Y2Y1VzRHOGdjWjl4dXdKdkcxZTd3Tms4S0NndCIsInN1YiI6ImRpZDprZXk6ejZNa2lpZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkIiwiZXhwIjo5OTk5OTk5OTk5LCJpYXQiOjAsInZjIjp7IkBjb250ZXh0IjoiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6Nk1raWlleW9MTVNWc0pBWnY3SmplNXdXU2tERXltVWdreUY4a2JjcmpacFgzcWQiLCJmaXJzdF9uYW1lIjoiRmVycmlzIiwibGFzdF9uYW1lIjoiUnVzdGFjZWFuIn0sImlzc3VlciI6ImRpZDprZXk6ejZNa2dFODROQ01wTWVBeDlqSzljZjVXNEc4Z2NaOXh1d0p2RzFlN3dOazhLQ2d0IiwiaXNzdWFuY2VEYXRlIjoiMjAxMC0wMS0wMVQwMDowMDowMFoifX0.d4QN73vDtZu79RP6GldHObu6rGsjidkLYp0XMRQNbNPY75LJoSv2iXk2Rz5M-VMBZGSU3YPZHytlrKBjxr1IBQ\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{HOST}}/v0/holder/credentials",
"host": [
"{{HOST}}"
],
"path": [
"v0",
"holder",
"credentials"
]
}
},
"response": []
},
{
"name": "Holder Credential by ID",
"request": {
Expand Down Expand Up @@ -1521,6 +1560,28 @@
"response": []
}
]
},
{
"name": "_monitoring",
"item": [
{
"name": "Liveness probe",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{HOST}}/healthz",
"host": [
"{{HOST}}"
],
"path": [
"healthz"
]
}
},
"response": []
}
]
}
],
"event": [
Expand Down
45 changes: 42 additions & 3 deletions agent_api_rest/src/holder/holder/credentials/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use agent_holder::state::HolderState;
use agent_shared::handlers::query_handler;
use agent_holder::{credential::command::CredentialCommand, state::HolderState};
use agent_shared::handlers::{command_handler, query_handler};
use axum::{
extract::{Path, State},
response::{IntoResponse, Response},
Json,
};
use hyper::StatusCode;
use serde_json::json;
use identity_credential::credential::Jwt;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use tracing::info;

/// Get all credentials
///
Expand All @@ -32,6 +35,42 @@ pub(crate) async fn credentials(State(state): State<HolderState>) -> Response {
}
}

#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HolderCredentialsEndpointRequest {
pub credential: Jwt,
}

#[axum_macros::debug_handler]
pub(crate) async fn post_credentials(State(state): State<HolderState>, Json(payload): Json<Value>) -> Response {
info!("Request Body: {}", payload);

let Ok(HolderCredentialsEndpointRequest { credential }) = serde_json::from_value(payload) else {
return (StatusCode::BAD_REQUEST, "invalid payload").into_response();
};

let holder_credential_id = uuid::Uuid::new_v4().to_string();

let command = CredentialCommand::AddCredential {
holder_credential_id: holder_credential_id.clone(),
received_offer_id: None,
credential,
};

// Add the credential.
if command_handler(&holder_credential_id, &state.command.credential, command)
.await
.is_err()
{
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}

match query_handler(&holder_credential_id, &state.query.holder_credential).await {
Ok(Some(holder_credential_view)) => (StatusCode::OK, Json(holder_credential_view)).into_response(),
_ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}
}

#[axum_macros::debug_handler]
pub(crate) async fn credential(State(state): State<HolderState>, Path(holder_credential_id): Path<String>) -> Response {
match query_handler(&holder_credential_id, &state.query.holder_credential).await {
Expand Down
2 changes: 1 addition & 1 deletion agent_api_rest/src/holder/holder/offers/accept.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub(crate) async fn accept(State(state): State<HolderState>, Path(received_offer
{
let command = CredentialCommand::AddCredential {
holder_credential_id: holder_credential_id.clone(),
received_offer_id: received_offer_id.clone(),
received_offer_id: Some(received_offer_id.clone()),
credential,
};

Expand Down
4 changes: 2 additions & 2 deletions agent_api_rest/src/holder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use agent_holder::state::HolderState;
use axum::routing::get;
use axum::{routing::post, Router};
use holder::{
credentials::credential,
credentials::{credential, post_credentials},
presentations::{get_presentations, post_presentations, presentation, presentation_signed::presentation_signed},
};

Expand All @@ -21,7 +21,7 @@ pub fn router(holder_state: HolderState) -> Router {
.nest(
API_VERSION,
Router::new()
.route("/holder/credentials", get(credentials))
.route("/holder/credentials", get(credentials).post(post_credentials))
.route("/holder/credentials/:credential_id", get(credential))
.route("/holder/presentations", get(get_presentations).post(post_presentations))
.route("/holder/presentations/:presentation_id", get(presentation))
Expand Down
5 changes: 4 additions & 1 deletion agent_api_rest/src/issuance/credential_issuer/credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ mod tests {
tests::{BASE_URL, CREDENTIAL_CONFIGURATION_ID, OFFER_ID},
};
use agent_event_publisher_http::EventPublisherHttp;
use agent_issuance::credential::aggregate::CredentialExpiry;
use agent_issuance::{offer::event::OfferEvent, startup_commands::startup_commands, state::initialize};
use agent_secret_manager::service::Service;
use agent_shared::config::{set_config, Events};
Expand All @@ -202,7 +203,7 @@ mod tests {
Mock, MockServer, ResponseTemplate,
};

const CREDENTIAL_JWT: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2dFODROQ01wTWVBeDlqSzljZjVXNEc4Z2NaOXh1d0p2RzFlN3dOazhLQ2d0I3o2TWtnRTg0TkNNcE1lQXg5aks5Y2Y1VzRHOGdjWjl4dXdKdkcxZTd3Tms4S0NndCJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtnRTg0TkNNcE1lQXg5aks5Y2Y1VzRHOGdjWjl4dXdKdkcxZTd3Tms4S0NndCIsInN1YiI6ImRpZDprZXk6ejZNa2lpZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkIiwiZXhwIjo5OTk5OTk5OTk5LCJpYXQiOjAsInZjIjp7IkBjb250ZXh0IjoiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6Nk1raWlleW9MTVNWc0pBWnY3SmplNXdXU2tERXltVWdreUY4a2JjcmpacFgzcWQiLCJmaXJzdF9uYW1lIjoiRmVycmlzIiwibGFzdF9uYW1lIjoiUnVzdGFjZWFuIn0sImlzc3VlciI6ImRpZDprZXk6ejZNa2dFODROQ01wTWVBeDlqSzljZjVXNEc4Z2NaOXh1d0p2RzFlN3dOazhLQ2d0IiwiaXNzdWFuY2VEYXRlIjoiMjAxMC0wMS0wMVQwMDowMDowMFoifX0.d4QN73vDtZu79RP6GldHObu6rGsjidkLYp0XMRQNbNPY75LJoSv2iXk2Rz5M-VMBZGSU3YPZHytlrKBjxr1IBQ";
const CREDENTIAL_JWT: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2dFODROQ01wTWVBeDlqSzljZjVXNEc4Z2NaOXh1d0p2RzFlN3dOazhLQ2d0I3o2TWtnRTg0TkNNcE1lQXg5aks5Y2Y1VzRHOGdjWjl4dXdKdkcxZTd3Tms4S0NndCJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtnRTg0TkNNcE1lQXg5aks5Y2Y1VzRHOGdjWjl4dXdKdkcxZTd3Tms4S0NndCIsInN1YiI6ImRpZDprZXk6ejZNa2lpZXlvTE1TVnNKQVp2N0pqZTV3V1NrREV5bVVna3lGOGtiY3JqWnBYM3FkIiwibmJmIjoxMjYyMzA0MDAwLCJpYXQiOjEyNjIzMDQwMDAsInZjIjp7IkBjb250ZXh0IjoiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6Nk1raWlleW9MTVNWc0pBWnY3SmplNXdXU2tERXltVWdreUY4a2JjcmpacFgzcWQiLCJmaXJzdF9uYW1lIjoiRmVycmlzIiwibGFzdF9uYW1lIjoiUnVzdGFjZWFuIn0sImlzc3VlciI6ImRpZDprZXk6ejZNa2dFODROQ01wTWVBeDlqSzljZjVXNEc4Z2NaOXh1d0p2RzFlN3dOazhLQ2d0IiwiaXNzdWFuY2VEYXRlIjoiMjAxMC0wMS0wMVQwMDowMDowMFoifX0.9IMQOMPD3V350XXlMthINwIT38gUC6WsPHKFpuR5hJ0w2DArrY5pjf2nG-_Ba5sSa3utKcc0QPHMaMCBPLpdAw";

trait CredentialEventTrigger {
async fn prepare_credential_event_trigger(
Expand Down Expand Up @@ -245,6 +246,7 @@ mod tests {
credential: json!(CREDENTIAL_JWT),
is_signed: true,
credential_configuration_id: CREDENTIAL_CONFIGURATION_ID.to_string(),
expires_at: CredentialExpiry::Never,
}
} else {
// ...or else, submitting the data that will be signed inside `UniCore`.
Expand All @@ -259,6 +261,7 @@ mod tests {
}),
is_signed: false,
credential_configuration_id: CREDENTIAL_CONFIGURATION_ID.to_string(),
expires_at: CredentialExpiry::Never,
}
};

Expand Down
8 changes: 6 additions & 2 deletions agent_api_rest/src/issuance/credentials.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::API_VERSION;
use agent_issuance::{
credential::{command::CredentialCommand, entity::Data, views::CredentialView},
credential::{aggregate::CredentialExpiry, command::CredentialCommand, entity::Data, views::CredentialView},
offer::command::OfferCommand,
server_config::queries::ServerConfigView,
state::{IssuanceState, SERVER_CONFIG_ID},
Expand Down Expand Up @@ -57,6 +57,7 @@ pub struct CredentialsEndpointRequest {
#[serde(default)]
pub is_signed: bool,
pub credential_configuration_id: String,
pub expires_at: CredentialExpiry,
}

/// Create a new credential
Expand Down Expand Up @@ -95,6 +96,7 @@ pub(crate) async fn credentials(
credential: data,
is_signed,
credential_configuration_id,
expires_at,
}) = serde_json::from_value(payload)
else {
return (StatusCode::BAD_REQUEST, "invalid payload").into_response();
Expand Down Expand Up @@ -142,6 +144,7 @@ pub(crate) async fn credentials(
credential_id: credential_id.clone(),
data: Data { raw: data },
credential_configuration,
expires_at,
}
};

Expand Down Expand Up @@ -268,7 +271,8 @@ pub mod tests {
"credential": {
"credentialSubject": CREDENTIAL_SUBJECT.clone()
},
"credentialConfigurationId": CREDENTIAL_CONFIGURATION_ID
"credentialConfigurationId": CREDENTIAL_CONFIGURATION_ID,
"expiresAt": "never"
}))
.unwrap(),
))
Expand Down
Loading

0 comments on commit 27856c4

Please sign in to comment.