diff --git a/Cargo.lock b/Cargo.lock index d8eff9b5..e22b3fe1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,8 +316,10 @@ version = "0.1.0" dependencies = [ "agent_holder", "agent_issuance", + "agent_library", "agent_secret_manager", "agent_shared", + "agent_store", "anyhow", "async-std", "async-trait", @@ -343,6 +345,7 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", "thiserror 1.0.69", + "tokio", "tracing", "tracing-test", "types-ob-v3", @@ -359,6 +362,7 @@ dependencies = [ "async-trait", "chrono", "cqrs-es", + "oid4vci", "rstest", "serde", "serde_json", diff --git a/agent_api_rest/openapi.yaml b/agent_api_rest/openapi.yaml index da7fc56c..5fed5dc1 100644 --- a/agent_api_rest/openapi.yaml +++ b/agent_api_rest/openapi.yaml @@ -366,99 +366,6 @@ paths: description: Authorization Request not found # Issuance endpoints - /v0/credential-configurations: - post: - tags: - - Issuance - summary: Create a new Credential Configuration - description: Creates a new credential configuration for the issuer - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - credential_configuration_id: - type: string - example: w3c_vc_credential - format: - type: string - example: jwt_vc_json - credential_definition: - type: object - properties: - type: - type: array - items: - type: string - example: VerifiableCredential - example: - - VerifiableCredential - display: - type: array - items: - type: object - properties: - locale: - type: string - example: en - logo: - type: object - properties: - alt_text: - type: string - example: UniCore Logo - uri: - type: string - example: https://impierce.com/images/logo-blue.png - name: - type: string - example: Identity Credential - example: - - locale: en - logo: - alt_text: UniCore Logo - uri: https://impierce.com/images/logo-blue.png - name: Identity Credential - required: - - credential_configuration_id - - format - - credential_definition - examples: - openbadgesv3_credential_configurations: - summary: Open Badges 3.0 - value: - credential_configuration_id: openbadge_credential - credential_definition: - type: - - VerifiableCredential - - OpenBadgeCredential - display: - - locale: en - logo: - alt_text: UniCore Logo - uri: https://impierce.com/images/logo-blue.png - name: Identity Credential - format: jwt_vc_json - w3c_vc_credential_configurations: - summary: W3C VC Data Model - value: - credential_configuration_id: w3c_vc_credential - credential_definition: - type: - - VerifiableCredential - display: - - locale: en - logo: - alt_text: UniCore Logo - uri: https://impierce.com/images/logo-blue.png - name: Identity Credential - format: jwt_vc_json - responses: - "200": - description: A Credential Configuration has been successfully added to the Credential Issuer Metadata - /v0/offers/send: post: summary: Send an offer diff --git a/agent_api_rest/postman/ssi-agent.postman_collection.json b/agent_api_rest/postman/ssi-agent.postman_collection.json index 077b35b2..3c8edc9b 100644 --- a/agent_api_rest/postman/ssi-agent.postman_collection.json +++ b/agent_api_rest/postman/ssi-agent.postman_collection.json @@ -430,33 +430,6 @@ } }, "response": [] - }, - { - "name": "Create a new Credential Configuration", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"credential_configuration_id\": \"002\",\n \"format\": \"jwt_vc_json\",\n \"credential_definition\": {\n \"type\": [\"VerifiableCredential\"]\n },\n \"display\": [\n {\n \"name\": \"Verifiable Credential\",\n \"locale\": \"en\",\n \"logo\": {\n \"uri\": \"https://www.impierce.com/external/impierce-logo.png\",\n \"alt_text\": \"Impierce Logo\"\n }\n }\n ]\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{HOST}}/v0/credential-configurations", - "host": [ - "{{HOST}}" - ], - "path": [ - "v0", - "credential-configurations" - ] - } - }, - "response": [] } ] }, diff --git a/agent_api_rest/src/authorization/authorization_server/authorize.rs b/agent_api_rest/src/authorization/authorization_server/authorize.rs index 9eeb91d4..2583ed88 100644 --- a/agent_api_rest/src/authorization/authorization_server/authorize.rs +++ b/agent_api_rest/src/authorization/authorization_server/authorize.rs @@ -12,10 +12,11 @@ use axum::{ }; use http::header; use oid4vci::wallet::AuthorizationRequestByReference; +use std::sync::Arc; #[axum_macros::debug_handler] pub(crate) async fn authorize( - State(state): State, + State(state): State>, Query(authorization_request): Query, ) -> Result { match OAuth2AuthorizationService::handle_authorization_request(&state, authorization_request) @@ -109,7 +110,7 @@ pub mod tests { #[serial_test::serial] #[tokio::test] async fn test_authorization_endpoint() { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); agent_issuance::state::initialize(&issuance_state).await.unwrap(); @@ -120,7 +121,8 @@ pub mod tests { let AuthorizationCode { issuer_state, .. } = authorization_code.unwrap(); let issuer_state = issuer_state.unwrap(); - let authorization_state = authorization_state(&InMemory, Service::default(), Default::default()).await; + let authorization_state = + Arc::new(authorization_state(&InMemory, Service::default(), Default::default()).await); agent_authorization::state::initialize(&authorization_state) .await .unwrap(); diff --git a/agent_api_rest/src/authorization/authorization_server/consent.rs b/agent_api_rest/src/authorization/authorization_server/consent.rs index c0fcca18..5591a809 100644 --- a/agent_api_rest/src/authorization/authorization_server/consent.rs +++ b/agent_api_rest/src/authorization/authorization_server/consent.rs @@ -12,11 +12,12 @@ use axum::{ }; use http::{header, StatusCode}; use serde::{Deserialize, Serialize}; +use std::sync::Arc; use tracing::info; #[axum_macros::debug_handler] pub(crate) async fn get_consent( - State(state): State, + State(state): State>, StringifiedQuery(GetConsentQuery { request_uri }): StringifiedQuery, ) -> Result { let ConsentPageViewModel { @@ -47,7 +48,7 @@ pub struct ConsentForm { // TODO: investigate replay attacks as described here: https://github.com/impierce/ssi-agent/issues/241 pub async fn post_consent( - State(state): State, + State(state): State>, Form(ConsentForm { client_id, request_uri, diff --git a/agent_api_rest/src/authorization/authorization_server/par.rs b/agent_api_rest/src/authorization/authorization_server/par.rs index bf843e4e..8d631d72 100644 --- a/agent_api_rest/src/authorization/authorization_server/par.rs +++ b/agent_api_rest/src/authorization/authorization_server/par.rs @@ -7,10 +7,11 @@ use axum::{ response::{IntoResponse, Response}, }; use oid4vci::authorization_request::AuthorizationRequest; +use std::sync::Arc; #[axum_macros::debug_handler] pub(crate) async fn par( - State(state): State, + State(state): State>, StringifiedForm(pushed_authorization_request): StringifiedForm, ) -> Result { let pushed_authorization_response = @@ -103,7 +104,7 @@ pub mod tests { #[serial_test::serial] #[tokio::test] async fn test_pushed_authorization_request_endpoint() { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); agent_issuance::state::initialize(&issuance_state).await.unwrap(); @@ -114,7 +115,8 @@ pub mod tests { let AuthorizationCode { issuer_state, .. } = authorization_code.unwrap(); let issuer_state = issuer_state.unwrap(); - let authorization_state = authorization_state(&InMemory, Service::default(), Default::default()).await; + let authorization_state = + Arc::new(authorization_state(&InMemory, Service::default(), Default::default()).await); agent_authorization::state::initialize(&authorization_state) .await .unwrap(); diff --git a/agent_api_rest/src/authorization/authorization_server/token.rs b/agent_api_rest/src/authorization/authorization_server/token.rs index 1fb9af68..bc8298ef 100644 --- a/agent_api_rest/src/authorization/authorization_server/token.rs +++ b/agent_api_rest/src/authorization/authorization_server/token.rs @@ -9,10 +9,11 @@ use axum::{ Form, }; use oid4vci::token_request::TokenRequest; +use std::sync::Arc; #[axum_macros::debug_handler] pub(crate) async fn token( - State((authorization_state, issuance_state)): State<(AuthorizationState, IssuanceState)>, + State((authorization_state, issuance_state)): State<(Arc, Arc)>, Form(token_request): Form, ) -> Result { let token_response = @@ -138,7 +139,7 @@ pub mod tests { #[serial_test::serial] #[tokio::test] async fn test_token_endpoint(#[case] is_pre_authorized: bool) { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); agent_issuance::state::initialize(&issuance_state).await.unwrap(); @@ -153,7 +154,8 @@ pub mod tests { credentials(&mut app, &credential_configuration_id).await; let grants = offers(&mut app, &credential_configuration_id).await.unwrap(); - let authorization_state = authorization_state(&InMemory, Service::default(), Default::default()).await; + let authorization_state = + Arc::new(authorization_state(&InMemory, Service::default(), Default::default()).await); agent_authorization::state::initialize(&authorization_state) .await diff --git a/agent_api_rest/src/authorization/mod.rs b/agent_api_rest/src/authorization/mod.rs index 5b97b4b9..e4df7671 100644 --- a/agent_api_rest/src/authorization/mod.rs +++ b/agent_api_rest/src/authorization/mod.rs @@ -9,8 +9,9 @@ use agent_issuance::state::IssuanceState; use authorization_server::{authorize::authorize, par::par, token::token}; use axum::routing::get; use axum::{routing::post, Router}; +use std::sync::Arc; -pub fn router((authorization_state, issuance_state): (AuthorizationState, IssuanceState)) -> Router { +pub fn router((authorization_state, issuance_state): (Arc, Arc)) -> Router { Router::new() .nest(API_VERSION, Router::new()) .route("/auth/consent", get(get_consent).post(post_consent)) diff --git a/agent_api_rest/src/holder/holder/credentials/mod.rs b/agent_api_rest/src/holder/holder/credentials/mod.rs index bcff586a..d26ae8d7 100644 --- a/agent_api_rest/src/holder/holder/credentials/mod.rs +++ b/agent_api_rest/src/holder/holder/credentials/mod.rs @@ -9,9 +9,10 @@ use http_api_problem::ApiError; use hyper::StatusCode; use identity_credential::credential::Jwt; use serde::{Deserialize, Serialize}; +use std::sync::Arc; #[axum_macros::debug_handler] -pub(crate) async fn credentials(State(state): State) -> Result { +pub(crate) async fn credentials(State(state): State>) -> Result { let all_credentials = query_handler("all_holder_credentials", &state.query.all_holder_credentials) .await? .map(|all_credentials_view| all_credentials_view.credentials.into_values().collect::>()) @@ -28,7 +29,7 @@ pub struct HolderCredentialsEndpointRequest { #[axum_macros::debug_handler] pub(crate) async fn post_credentials( - State(state): State, + State(state): State>, Json(HolderCredentialsEndpointRequest { credential }): Json, ) -> Result { let holder_credential_id = uuid::Uuid::new_v4().to_string(); @@ -50,7 +51,7 @@ pub(crate) async fn post_credentials( #[axum_macros::debug_handler] pub(crate) async fn credential( - State(state): State, + State(state): State>, Path(holder_credential_id): Path, ) -> Result { query_handler(&holder_credential_id, &state.query.holder_credential) diff --git a/agent_api_rest/src/holder/holder/offers/accept.rs b/agent_api_rest/src/holder/holder/offers/accept.rs index 018640d1..0ab407ef 100644 --- a/agent_api_rest/src/holder/holder/offers/accept.rs +++ b/agent_api_rest/src/holder/holder/offers/accept.rs @@ -11,10 +11,11 @@ use axum::{ }; use http_api_problem::ApiError; use hyper::StatusCode; +use std::sync::Arc; #[axum_macros::debug_handler] pub(crate) async fn accept( - State(state): State, + State(state): State>, Path(received_offer_id): Path, ) -> Result { // TODO: General note that also applies to other endpoints: currently we are using Application Layer logic in the diff --git a/agent_api_rest/src/holder/holder/offers/mod.rs b/agent_api_rest/src/holder/holder/offers/mod.rs index c6f292a8..0c1fc8ce 100644 --- a/agent_api_rest/src/holder/holder/offers/mod.rs +++ b/agent_api_rest/src/holder/holder/offers/mod.rs @@ -10,9 +10,10 @@ use axum::{ }; use http_api_problem::ApiError; use hyper::StatusCode; +use std::sync::Arc; #[axum_macros::debug_handler] -pub(crate) async fn offers(State(state): State) -> Result { +pub(crate) async fn offers(State(state): State>) -> Result { let all_received_offers = query_handler("all_received_offers", &state.query.all_received_offers) .await? .map(|all_received_offers_view| { @@ -28,7 +29,7 @@ pub(crate) async fn offers(State(state): State) -> Result, + State(state): State>, Path(received_offer_id): Path, ) -> Result { query_handler(&received_offer_id, &state.query.received_offer) diff --git a/agent_api_rest/src/holder/holder/offers/reject.rs b/agent_api_rest/src/holder/holder/offers/reject.rs index f41246cf..315eb00c 100644 --- a/agent_api_rest/src/holder/holder/offers/reject.rs +++ b/agent_api_rest/src/holder/holder/offers/reject.rs @@ -6,10 +6,11 @@ use axum::{ }; use http_api_problem::ApiError; use hyper::StatusCode; +use std::sync::Arc; #[axum_macros::debug_handler] pub(crate) async fn reject( - State(state): State, + State(state): State>, Path(received_offer_id): Path, ) -> Result { let command = OfferCommand::RejectCredentialOffer { diff --git a/agent_api_rest/src/holder/holder/presentations/mod.rs b/agent_api_rest/src/holder/holder/presentations/mod.rs index 72ab9bed..3129e779 100644 --- a/agent_api_rest/src/holder/holder/presentations/mod.rs +++ b/agent_api_rest/src/holder/holder/presentations/mod.rs @@ -12,9 +12,10 @@ use axum::{ use http_api_problem::ApiError; use hyper::StatusCode; use serde::{Deserialize, Serialize}; +use std::sync::Arc; #[axum_macros::debug_handler] -pub(crate) async fn get_presentations(State(state): State) -> Result { +pub(crate) async fn get_presentations(State(state): State>) -> Result { let all_presentations = query_handler("all_presentations", &state.query.all_presentations) .await? .map(|all_presentations_view| all_presentations_view.presentations.into_values().collect::>()) @@ -25,7 +26,7 @@ pub(crate) async fn get_presentations(State(state): State) -> Resul #[axum_macros::debug_handler] pub(crate) async fn presentation( - State(state): State, + State(state): State>, Path(presentation_id): Path, ) -> Result { query_handler(&presentation_id, &state.query.presentation) @@ -42,7 +43,7 @@ pub struct PresentationsEndpointRequest { #[axum_macros::debug_handler] pub(crate) async fn post_presentations( - State(state): State, + State(state): State>, Json(PresentationsEndpointRequest { credential_ids }): Json, ) -> Result { let mut credentials = vec![]; diff --git a/agent_api_rest/src/holder/holder/presentations/presentation_signed.rs b/agent_api_rest/src/holder/holder/presentations/presentation_signed.rs index 0b232616..9ffe6bbb 100644 --- a/agent_api_rest/src/holder/holder/presentations/presentation_signed.rs +++ b/agent_api_rest/src/holder/holder/presentations/presentation_signed.rs @@ -6,10 +6,11 @@ use axum::{ }; use http_api_problem::ApiError; use hyper::{header, StatusCode}; +use std::sync::Arc; #[axum_macros::debug_handler] pub(crate) async fn presentation_signed( - State(state): State, + State(state): State>, Path(presentation_id): Path, ) -> Result { match query_handler(&presentation_id, &state.query.presentation).await? { diff --git a/agent_api_rest/src/holder/mod.rs b/agent_api_rest/src/holder/mod.rs index 0604aa78..1dbb3071 100644 --- a/agent_api_rest/src/holder/mod.rs +++ b/agent_api_rest/src/holder/mod.rs @@ -17,8 +17,9 @@ use holder::{ credentials::{credential, post_credentials}, presentations::{get_presentations, post_presentations, presentation, presentation_signed::presentation_signed}, }; +use std::sync::Arc; -pub fn router(holder_state: HolderState) -> Router { +pub fn router(holder_state: Arc) -> Router { Router::new() .nest( API_VERSION, diff --git a/agent_api_rest/src/holder/openid4vci/mod.rs b/agent_api_rest/src/holder/openid4vci/mod.rs index f68e7b6c..ea9ef526 100644 --- a/agent_api_rest/src/holder/openid4vci/mod.rs +++ b/agent_api_rest/src/holder/openid4vci/mod.rs @@ -9,11 +9,12 @@ use http_api_problem::ApiError; use hyper::StatusCode; use oid4vci::credential_offer::CredentialOffer; use serde_json::Value; +use std::sync::Arc; use tracing::info; #[axum_macros::debug_handler] pub(crate) async fn offers_params( - State(state): State, + State(state): State>, // TODO: Can this be changed to `StringifiedForm`? Form(payload): Form, ) -> Result { diff --git a/agent_api_rest/src/identity/connections/mod.rs b/agent_api_rest/src/identity/connections/mod.rs index e3b82529..42acf1f6 100644 --- a/agent_api_rest/src/identity/connections/mod.rs +++ b/agent_api_rest/src/identity/connections/mod.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::handlers::{command_handler, query_handler}; use crate::API_VERSION; use agent_identity::{connection::command::ConnectionCommand, state::IdentityState}; @@ -28,7 +30,7 @@ pub struct PostConnectionsEndpointRequest { #[axum_macros::debug_handler] pub(crate) async fn post_connections( - State(state): State, + State(state): State>, Json(PostConnectionsEndpointRequest { alias, domain, @@ -76,7 +78,7 @@ pub struct GetConnectionsEndpointRequest { #[axum_macros::debug_handler] pub(crate) async fn get_connections( - State(state): State, + State(state): State>, Form(GetConnectionsEndpointRequest { alias, domain, did }): Form, ) -> Result { debug!("Request Params - alias: {alias:?}, domain: {domain:?}, did: {did:?}"); @@ -107,7 +109,7 @@ pub(crate) async fn get_connections( #[axum_macros::debug_handler] pub(crate) async fn get_connection( - State(state): State, + State(state): State>, Path(connection_id): Path, ) -> Result { query_handler(&connection_id, &state.query.connection) diff --git a/agent_api_rest/src/identity/documents/mod.rs b/agent_api_rest/src/identity/documents/mod.rs index 2c757a09..adf3ac62 100644 --- a/agent_api_rest/src/identity/documents/mod.rs +++ b/agent_api_rest/src/identity/documents/mod.rs @@ -9,6 +9,7 @@ use axum::{ use http_api_problem::ApiError; use hyper::StatusCode; use serde::{Deserialize, Serialize}; +use std::sync::Arc; use tracing::debug; #[derive(Deserialize, Serialize)] @@ -18,7 +19,7 @@ pub struct GetDocumentsEndpoint { } pub(crate) async fn get_documents( - State(state): State, + State(state): State>, Form(GetDocumentsEndpoint { did_method }): Form, ) -> Result { debug!("Request Params - did_method: {did_method:?}"); @@ -45,7 +46,7 @@ pub(crate) async fn get_documents( #[axum_macros::debug_handler] pub(crate) async fn get_document( - State(state): State, + State(state): State>, Path(document_id): Path, ) -> Result { query_handler(&document_id, &state.query.document) diff --git a/agent_api_rest/src/identity/mod.rs b/agent_api_rest/src/identity/mod.rs index f3cbfe6b..6060268b 100644 --- a/agent_api_rest/src/identity/mod.rs +++ b/agent_api_rest/src/identity/mod.rs @@ -6,6 +6,8 @@ pub mod well_known; pub mod error; +use std::sync::Arc; + use agent_identity::state::IdentityState; use axum::{ routing::{get, post}, @@ -21,7 +23,7 @@ use crate::{ API_VERSION, }; -pub fn router(identity_state: IdentityState) -> Router { +pub fn router(identity_state: Arc) -> Router { Router::new() .nest( API_VERSION, diff --git a/agent_api_rest/src/identity/profiles/mod.rs b/agent_api_rest/src/identity/profiles/mod.rs index 509964e6..521b0727 100644 --- a/agent_api_rest/src/identity/profiles/mod.rs +++ b/agent_api_rest/src/identity/profiles/mod.rs @@ -15,6 +15,7 @@ use http_api_problem::ApiError; use hyper::StatusCode; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; +use std::sync::Arc; #[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] @@ -29,7 +30,7 @@ pub struct PatchProfileEndpointRequest { #[axum_macros::debug_handler] pub(crate) async fn patch_profile( - State(state): State, + State(state): State>, Json(PatchProfileEndpointRequest { display_name, logo, @@ -81,7 +82,7 @@ struct GetProfileEndpointResponse { } #[axum_macros::debug_handler] -pub(crate) async fn get_profile(State(state): State) -> Result { +pub(crate) async fn get_profile(State(state): State>) -> Result { query_handler(PROFILE_ID, &state.query.profile) .await? .map(|profile_view| { diff --git a/agent_api_rest/src/identity/services/linked_vp.rs b/agent_api_rest/src/identity/services/linked_vp.rs index 6d258717..db5c1a78 100644 --- a/agent_api_rest/src/identity/services/linked_vp.rs +++ b/agent_api_rest/src/identity/services/linked_vp.rs @@ -13,6 +13,7 @@ use axum::{ use http_api_problem::ApiError; use hyper::StatusCode; use serde::{Deserialize, Serialize}; +use std::sync::Arc; #[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] @@ -22,7 +23,7 @@ pub struct LinkedVPEndpointRequest { #[axum_macros::debug_handler] pub(crate) async fn linked_vp( - State(state): State, + State(state): State>, Json(LinkedVPEndpointRequest { presentation_ids }): Json, ) -> Result { let service_id = "linked-verifiable-presentation-service".to_string(); diff --git a/agent_api_rest/src/identity/services/mod.rs b/agent_api_rest/src/identity/services/mod.rs index 3cd2a767..ce81bf4f 100644 --- a/agent_api_rest/src/identity/services/mod.rs +++ b/agent_api_rest/src/identity/services/mod.rs @@ -9,9 +9,10 @@ use axum::{ }; use http_api_problem::ApiError; use hyper::StatusCode; +use std::sync::Arc; #[axum_macros::debug_handler] -pub(crate) async fn services(State(state): State) -> Result { +pub(crate) async fn services(State(state): State>) -> Result { let all_services = query_handler("all_services", &state.query.all_services) .await? .map(|all_services_view| all_services_view.services.into_values().collect::>()) @@ -22,7 +23,7 @@ pub(crate) async fn services(State(state): State) -> Result, + State(state): State>, Path(service_id): Path, ) -> Result { query_handler(&service_id, &state.query.service) diff --git a/agent_api_rest/src/identity/well_known/did.rs b/agent_api_rest/src/identity/well_known/did.rs index a3d53b12..e8299e9c 100644 --- a/agent_api_rest/src/identity/well_known/did.rs +++ b/agent_api_rest/src/identity/well_known/did.rs @@ -8,9 +8,10 @@ use axum::{ }; use http_api_problem::ApiError; use hyper::StatusCode; +use std::sync::Arc; #[axum_macros::debug_handler] -pub(crate) async fn did(State(state): State) -> Result { +pub(crate) async fn did(State(state): State>) -> Result { query_handler("all_documents", &state.query.all_documents) .await? .and_then(|all_documents_view| { diff --git a/agent_api_rest/src/identity/well_known/did_configuration.rs b/agent_api_rest/src/identity/well_known/did_configuration.rs index bf70b91c..95ae9840 100644 --- a/agent_api_rest/src/identity/well_known/did_configuration.rs +++ b/agent_api_rest/src/identity/well_known/did_configuration.rs @@ -10,9 +10,10 @@ use axum::{ }; use http_api_problem::ApiError; use hyper::StatusCode; +use std::sync::Arc; #[axum_macros::debug_handler] -pub(crate) async fn did_configuration(State(state): State) -> Result { +pub(crate) async fn did_configuration(State(state): State>) -> Result { // Get the DID Configuration Resource if it exists. match query_handler(DOMAIN_LINKAGE_SERVICE_ID, &state.query.service).await? { Some(ServiceView { diff --git a/agent_api_rest/src/issuance/credential_configurations.rs b/agent_api_rest/src/issuance/credential_configurations.rs deleted file mode 100644 index 84274291..00000000 --- a/agent_api_rest/src/issuance/credential_configurations.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::handlers::command_handler; -use agent_issuance::server_config::command::ServerConfigCommand; -use agent_issuance::state::{IssuanceState, SERVER_CONFIG_ID}; -use agent_shared::config::CredentialConfiguration; -use axum::{ - extract::{Json, State}, - http::StatusCode, - response::{IntoResponse, Response}, -}; -use http_api_problem::ApiError; - -#[axum_macros::debug_handler] -pub(crate) async fn credential_configurations( - State(state): State, - Json(credential_configuration): Json, -) -> Result { - let command = ServerConfigCommand::UpdateCredentialConfiguration { - credential_configuration: credential_configuration.clone(), - provisioned: false, - }; - - command_handler(SERVER_CONFIG_ID, &state.command.server_config, command).await?; - - Ok((StatusCode::CREATED, Json(credential_configuration)).into_response()) -} diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index c511c152..8b0c99f4 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -21,6 +21,7 @@ use axum::{ use axum_auth::AuthBearer; use oid4vci::credential_request::CredentialRequest; use oid4vci::errors::CredentialErrorResponse; +use std::sync::Arc; use tokio::time::sleep; use tracing::error; @@ -28,7 +29,7 @@ const POLLING_INTERVAL_MS: u64 = 100; #[axum_macros::debug_handler] pub(crate) async fn credential( - State(state): State, + State(state): State>, AuthBearer(access_token): AuthBearer, Json(credential_request): Json, ) -> Result { @@ -351,7 +352,7 @@ pub mod tests { (None, Default::default()) }; - let issuance_state = issuance_state(&InMemory, Service::default(), issuance_event_publishers).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), issuance_event_publishers).await); agent_issuance::state::initialize(&issuance_state).await.unwrap(); let mut issuance_app = router(issuance_state.clone()); @@ -381,7 +382,8 @@ pub mod tests { let grants = offers(&mut issuance_app, &credential_configuration_id).await.unwrap(); - let authorization_state = authorization_state(&InMemory, Service::default(), Default::default()).await; + let authorization_state = + Arc::new(authorization_state(&InMemory, Service::default(), Default::default()).await); agent_authorization::state::initialize(&authorization_state) .await .unwrap(); diff --git a/agent_api_rest/src/issuance/credential_issuer/credential_offer.rs b/agent_api_rest/src/issuance/credential_issuer/credential_offer.rs index 3197e5d6..6ffbe220 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential_offer.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential_offer.rs @@ -8,10 +8,11 @@ use axum::{ use http_api_problem::ApiError; use hyper::StatusCode; use oid4vci::credential_offer::CredentialOffer; +use std::sync::Arc; #[axum_macros::debug_handler] pub(crate) async fn credential_offer_uri( - State(state): State, + State(state): State>, Path(offer_id): Path, ) -> Result { match query_handler(&offer_id, &state.query.offer).await? { diff --git a/agent_api_rest/src/issuance/credential_issuer/notification.rs b/agent_api_rest/src/issuance/credential_issuer/notification.rs index 6c7fb59f..0473688e 100644 --- a/agent_api_rest/src/issuance/credential_issuer/notification.rs +++ b/agent_api_rest/src/issuance/credential_issuer/notification.rs @@ -11,6 +11,7 @@ use axum_auth::AuthBearer; use oid4vci::errors::NotificationErrorResponse; use oid4vci::notification_request::NotificationRequest; use serde_json::json; +use std::sync::Arc; use tracing::info; /// The HTTP response MUST use the HTTP status code 400 (Bad Request) and set the content type to application/json. @@ -18,7 +19,7 @@ use tracing::info; #[axum_macros::debug_handler] pub async fn notification( - State(state): State, + State(state): State>, AuthBearer(access_token): AuthBearer, Json(raw_value): Json, ) -> Result { @@ -79,14 +80,15 @@ mod tests { #[serial_test::serial] #[tokio::test] async fn test_valid_notification_request() { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); agent_issuance::state::initialize(&issuance_state).await.unwrap(); let mut issuance_app = issuance::router(issuance_state.clone()); credentials(&mut issuance_app, "001").await; let grants = offers(&mut issuance_app, "001").await.unwrap(); - let authorization_state = authorization_state(&InMemory, Service::default(), Default::default()).await; + let authorization_state = + Arc::new(authorization_state(&InMemory, Service::default(), Default::default()).await); agent_authorization::state::initialize(&authorization_state) .await .unwrap(); @@ -117,14 +119,15 @@ mod tests { #[tokio::test] async fn test_invalid_notification_request() { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); agent_issuance::state::initialize(&issuance_state).await.unwrap(); let mut issuance_app = issuance::router(issuance_state.clone()); credentials(&mut issuance_app, "001").await; let grants = offers(&mut issuance_app, "001").await.unwrap(); - let authorization_state = authorization_state(&InMemory, Service::default(), Default::default()).await; + let authorization_state = + Arc::new(authorization_state(&InMemory, Service::default(), Default::default()).await); agent_authorization::state::initialize(&authorization_state) .await .unwrap(); diff --git a/agent_api_rest/src/issuance/credential_issuer/token_status_list.rs b/agent_api_rest/src/issuance/credential_issuer/token_status_list.rs index 962c0a2e..662b2baa 100644 --- a/agent_api_rest/src/issuance/credential_issuer/token_status_list.rs +++ b/agent_api_rest/src/issuance/credential_issuer/token_status_list.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use agent_issuance::{ credential::application::token_status_list_service::TokenStatusListService, state::IssuanceState, }; @@ -12,12 +14,12 @@ use oauth_tsl::relying_party::StatusListTokenResponseType; use crate::issuance::error::PublicError; pub async fn token_status_list( - State(state): State, + State(state): State>, Path(status_list_number): Path, ) -> Result { let token_status_list_service = TokenStatusListService {}; let compressed_jwt_token = token_status_list_service - .create_gzip_status_list_jwt_token(status_list_number, state) + .create_gzip_status_list_jwt_token(status_list_number, &state) .await .map_err(|_| PublicError::InternalServerError)?; @@ -37,6 +39,8 @@ pub async fn token_status_list( #[cfg(test)] pub mod tests { + use std::sync::Arc; + use agent_issuance::state::initialize; use agent_secret_manager::{service::Service, subject::Subject}; use agent_shared::config::{config, BITS_PER_STATUS, STATUS_LIST_BYTES_AMOUNT}; @@ -57,7 +61,7 @@ pub mod tests { /// The remainder of the test breaks down the Token Status List response in various steps and checks these steps one by one. #[tokio::test] pub async fn test_token_status_list() { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); initialize(&issuance_state).await.unwrap(); let relying_party_state = Subject::default(); diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs index acd93e97..ed3da5bc 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs @@ -9,10 +9,11 @@ use axum::{ response::{IntoResponse, Response}, }; use http_api_problem::ApiError; +use std::sync::Arc; // TODO: move this to `authorization/authorization_server/well_known.rs`! #[axum_macros::debug_handler] -pub(crate) async fn oauth_authorization_server(State(state): State) -> Result { +pub(crate) async fn oauth_authorization_server(State(state): State>) -> Result { match query_handler(SERVER_CONFIG_ID, &state.query.server_config).await? { Some(ServerConfigView { authorization_server_metadata, @@ -73,7 +74,7 @@ mod tests { #[tokio::test] async fn test_oauth_authorization_server_endpoint() { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); initialize(&issuance_state).await.unwrap(); let mut app = router(issuance_state); diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs index f9f03209..9b7d7d4a 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs @@ -11,9 +11,10 @@ use axum::{ }; use http_api_problem::ApiError; use serde_json::json; +use std::sync::Arc; #[axum_macros::debug_handler] -pub(crate) async fn openid_credential_issuer(State(state): State) -> Result { +pub(crate) async fn openid_credential_issuer(State(state): State>) -> Result { match query_handler(SERVER_CONFIG_ID, &state.query.server_config).await? { Some(ServerConfigView { mut credential_issuer_metadata, @@ -70,7 +71,7 @@ mod tests { #[tokio::test] async fn test_openid_credential_issuer_endpoint() { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); initialize(&issuance_state).await.unwrap(); let mut app = router(issuance_state); diff --git a/agent_api_rest/src/issuance/credentials.rs b/agent_api_rest/src/issuance/credentials.rs index 4210a053..fd2d4b82 100644 --- a/agent_api_rest/src/issuance/credentials.rs +++ b/agent_api_rest/src/issuance/credentials.rs @@ -18,13 +18,14 @@ use oauth_tsl::status_list::StatusType; use oid4vci::credential_offer::GrantType; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::sync::Arc; #[cfg(feature = "test_utils")] pub const TESTINDEX: usize = 123; #[axum_macros::debug_handler] pub(crate) async fn credential( - State(state): State, + State(state): State>, Path(credential_id): Path, ) -> Result { query_handler(&credential_id, &state.query.credential) @@ -46,7 +47,7 @@ pub struct CredentialsEndpointRequest { #[axum_macros::debug_handler] pub(crate) async fn credentials( - State(state): State, + State(state): State>, Json(CredentialsEndpointRequest { offer_id, credential, @@ -196,7 +197,7 @@ pub(crate) async fn credentials( } #[axum_macros::debug_handler] -pub(crate) async fn all_credentials(State(state): State) -> Result { +pub(crate) async fn all_credentials(State(state): State>) -> Result { let all_credentials = query_handler("all_credentials", &state.query.all_credentials) .await? .map(|all_credentials_view| all_credentials_view.credentials.into_values().collect::>()) @@ -213,7 +214,7 @@ pub struct PatchCredentialEndpointRequest { /// Currently, this endpoint only supports patching the CredentialStatus of a credential according to the IETF OAuth Token Status List spec. pub async fn patch_credential( - State(state): State, + State(state): State>, Path(credential_id): Path, Json(PatchCredentialEndpointRequest { credential_status: status, @@ -409,7 +410,7 @@ pub mod tests { #[tokio::test] async fn test_patch_credential() { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); initialize(&issuance_state).await.unwrap(); let mut app = router(issuance_state); @@ -420,7 +421,7 @@ pub mod tests { #[tokio::test] #[tracing_test::traced_test] async fn test_credentials_endpoint() { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); initialize(&issuance_state).await.unwrap(); let mut app = router(issuance_state); diff --git a/agent_api_rest/src/issuance/error.rs b/agent_api_rest/src/issuance/error.rs index 2e43d8f1..961856c1 100644 --- a/agent_api_rest/src/issuance/error.rs +++ b/agent_api_rest/src/issuance/error.rs @@ -109,20 +109,7 @@ impl IntoApiErrorExt for OfferError { impl IntoApiErrorExt for ServerConfigError { fn into_api_error(self) -> ApiError { - use ServerConfigError::*; - match self { - // UniCore API Problem Details - UpdateProvisionedCredentialConfigurationError => ApiError::builder(StatusCode::BAD_REQUEST) - .title("Update Provisioned Credential Configuration Error") - .type_url(type_url("issuance#update-provisioned-credential-configuration-error")) - .source(self) - .finish(), - RemoveProvisionedCredentialConfigurationError => ApiError::builder(StatusCode::BAD_REQUEST) - .title("Remove Provisioned Credential Configuration Error") - .type_url(type_url("issuance#remove-provisioned-credential-configuration-error")) - .source(self) - .finish(), - } + match self {} } } @@ -195,11 +182,7 @@ impl IntoPublicError for OfferError { impl IntoPublicError for ServerConfigError { fn into_public_error(self) -> PublicError { - use ServerConfigError::*; - match self { - UpdateProvisionedCredentialConfigurationError => PublicError::InternalServerError, - RemoveProvisionedCredentialConfigurationError => PublicError::InternalServerError, - } + match self {} } } @@ -338,35 +321,5 @@ pub mod tests { "detail": "Credential Offer does not exist" }), ); - - assert_eq!( - into_json_value( - ServerConfigError::UpdateProvisionedCredentialConfigurationError - .into_api_error() - .into_axum_response() - ) - .await, - json!({ - "type": format!("{DOCUMENTATION_URL}problem-details/issuance#update-provisioned-credential-configuration-error"), - "title": "Update Provisioned Credential Configuration Error", - "status": 400, - "detail": "Cannot update provisioned credential configuration during runtime" - }), - ); - - assert_eq!( - into_json_value( - ServerConfigError::RemoveProvisionedCredentialConfigurationError - .into_api_error() - .into_axum_response() - ) - .await, - json!({ - "type": format!("{DOCUMENTATION_URL}problem-details/issuance#remove-provisioned-credential-configuration-error"), - "title": "Remove Provisioned Credential Configuration Error", - "status": 400, - "detail": "Cannot remove provisioned credential configuration during runtime" - }), - ); } } diff --git a/agent_api_rest/src/issuance/mod.rs b/agent_api_rest/src/issuance/mod.rs index 747966d6..ad084731 100644 --- a/agent_api_rest/src/issuance/mod.rs +++ b/agent_api_rest/src/issuance/mod.rs @@ -1,4 +1,3 @@ -pub mod credential_configurations; pub mod credential_issuer; pub mod credentials; pub mod offers; @@ -6,7 +5,6 @@ pub mod offers; pub mod error; use crate::issuance::{ - credential_configurations::credential_configurations, credential_issuer::{ credential::credential, credential_offer::credential_offer_uri, @@ -23,8 +21,9 @@ use crate::API_VERSION; use agent_issuance::state::IssuanceState; use axum::routing::get; use axum::{routing::post, Router}; +use std::sync::Arc; -pub fn router(issuance_state: IssuanceState) -> Router { +pub fn router(issuance_state: Arc) -> Router { Router::new() .nest( API_VERSION, @@ -34,7 +33,6 @@ pub fn router(issuance_state: IssuanceState) -> Router { "/credentials/{credential_id}", get(credentials::credential).patch(patch_credential), ) - .route("/credential-configurations", post(credential_configurations)) .route("/offers", post(offers).get(all_offers)) .route("/offers/{offer_id}", get(offer)) .route("/offers/send", post(send)), diff --git a/agent_api_rest/src/issuance/offers/mod.rs b/agent_api_rest/src/issuance/offers/mod.rs index 903fb5e7..b2fc3e11 100644 --- a/agent_api_rest/src/issuance/offers/mod.rs +++ b/agent_api_rest/src/issuance/offers/mod.rs @@ -17,6 +17,7 @@ use http_api_problem::ApiError; use hyper::header; use oid4vci::credential_offer::GrantType; use serde::{Deserialize, Serialize}; +use std::sync::Arc; #[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] @@ -28,7 +29,7 @@ pub struct OffersEndpointRequest { #[axum_macros::debug_handler] pub(crate) async fn offers( - State(state): State, + State(state): State>, Json(OffersEndpointRequest { offer_id, credential_configuration_ids, @@ -56,8 +57,8 @@ pub(crate) async fn offers( } let authorization = credential_configurations - .into_iter() - .find_map(|(credential_configuration_id, (_, _, authorization))| { + .into_values() + .find_map(|(credential_configuration_id, _, authorization)| { credential_configuration_ids .contains(&credential_configuration_id) .then_some(authorization) @@ -103,7 +104,7 @@ pub(crate) async fn offers( } #[axum_macros::debug_handler] -pub(crate) async fn all_offers(State(state): State) -> Result { +pub(crate) async fn all_offers(State(state): State>) -> Result { let all_offers = query_handler("all_offers", &state.query.all_offers) .await? .map(|all_offers_view| all_offers_view.offers.into_values().collect::>()) @@ -114,7 +115,7 @@ pub(crate) async fn all_offers(State(state): State) -> Result, + State(state): State>, Path(offer_id): Path, ) -> Result { query_handler(&offer_id, &state.query.offer) @@ -218,7 +219,7 @@ pub mod tests { #[tokio::test] #[tracing_test::traced_test] async fn test_offers_endpoint() { - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); initialize(&issuance_state).await.unwrap(); let mut app = router(issuance_state); @@ -232,7 +233,7 @@ pub mod tests { #[tracing_test::traced_test] async fn test_offers_endpoint_by_reference() { set_config().credential_offer_by_value_enabled = false; - let issuance_state = issuance_state(&InMemory, Service::default(), Default::default()).await; + let issuance_state = Arc::new(issuance_state(&InMemory, Service::default(), Default::default()).await); initialize(&issuance_state).await.unwrap(); let mut app = router(issuance_state); diff --git a/agent_api_rest/src/issuance/offers/send.rs b/agent_api_rest/src/issuance/offers/send.rs index db5ed948..ef1425d2 100644 --- a/agent_api_rest/src/issuance/offers/send.rs +++ b/agent_api_rest/src/issuance/offers/send.rs @@ -8,6 +8,7 @@ use axum::{ use http_api_problem::ApiError; use hyper::StatusCode; use serde::{Deserialize, Serialize}; +use std::sync::Arc; use url::Url; #[derive(Deserialize, Serialize)] @@ -19,7 +20,7 @@ pub struct SendOfferEndpointRequest { #[axum_macros::debug_handler] pub(crate) async fn send( - State(state): State, + State(state): State>, Json(SendOfferEndpointRequest { offer_id, target_url }): Json, ) -> Result { let command = OfferCommand::SendCredentialOffer { diff --git a/agent_api_rest/src/lib.rs b/agent_api_rest/src/lib.rs index 452a3db9..aaeac8fe 100644 --- a/agent_api_rest/src/lib.rs +++ b/agent_api_rest/src/lib.rs @@ -27,7 +27,7 @@ use axum::{ }; use http_body_util::BodyExt as _; use hyper::StatusCode; -use std::time::Duration; +use std::{sync::Arc, time::Duration}; use tower::ServiceBuilder; use tower_http::trace::TraceLayer; use tracing::{debug, info, info_span, Span}; @@ -38,12 +38,12 @@ pub const DOCUMENTATION_URL: &str = "https://beta.docs.impierce.com/unicore/"; #[derive(Default)] pub struct ApplicationState { - pub identity_state: Option, - pub library_state: Option, - pub authorization_state: Option, - pub issuance_state: Option, - pub holder_state: Option, - pub verification_state: Option, + pub identity_state: Option>, + pub library_state: Option>, + pub authorization_state: Option>, + pub issuance_state: Option>, + pub holder_state: Option>, + pub verification_state: Option>, } pub fn app( diff --git a/agent_api_rest/src/library/mod.rs b/agent_api_rest/src/library/mod.rs index bf6ab91b..a9053377 100644 --- a/agent_api_rest/src/library/mod.rs +++ b/agent_api_rest/src/library/mod.rs @@ -3,13 +3,14 @@ pub mod templates; use agent_library::state::LibraryState; use axum::{routing::get, Router}; +use std::sync::Arc; use crate::{ library::templates::{get_template, get_templates, patch_template, post_templates}, API_VERSION, }; -pub fn router(library_state: LibraryState) -> Router { +pub fn router(library_state: Arc) -> Router { Router::new() .nest( API_VERSION, diff --git a/agent_api_rest/src/library/templates/mod.rs b/agent_api_rest/src/library/templates/mod.rs index bf7f809c..a29e28bd 100644 --- a/agent_api_rest/src/library/templates/mod.rs +++ b/agent_api_rest/src/library/templates/mod.rs @@ -1,8 +1,9 @@ use crate::handlers::{command_handler, query_handler}; use crate::API_VERSION; use agent_library::state::LibraryState; -pub use agent_library::template::aggregate::{CredentialFormat, Display, HolderType, Status, Visibility}; +use agent_library::template::aggregate::{CredentialFormat, Display, HolderType, Status, Template, Visibility}; use agent_library::template::command::TemplateCommand; +use agent_library::template::event::Authorization; use axum::{ extract::{Path, State}, response::{IntoResponse, Response}, @@ -11,12 +12,53 @@ use axum::{ use http_api_problem::ApiError; use hyper::{header, StatusCode}; use serde::{Deserialize, Serialize}; +use std::sync::Arc; use tracing::debug; +/// Data transfer object for Templates. +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TemplateDto { + #[serde(rename = "id")] + pub template_id: String, + pub title: Option, + pub display: Option, + pub credential_format: Option, + pub creator: Option, + pub holder_type: Option, + pub modified_at: Option, + pub tags: Vec, + pub status: Status, + pub visibility: Visibility, + pub description: Option, + pub r#type: Vec, + pub schema: Option, +} + +impl From