From 29011d3373864603b7e9290d2432c8003791df88 Mon Sep 17 00:00:00 2001 From: Adel Golghalyani Date: Fri, 3 Nov 2023 13:31:04 +0100 Subject: [PATCH 1/5] comments for config --- src/configuration.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/configuration.rs b/src/configuration.rs index 1c494b2..225552e 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -10,46 +10,68 @@ use subxt::{ use crate::tx::KiltConfig; #[derive(Deserialize, Debug, Clone, Parser)] +/// Represents the configuration settings required for running the attestation service. pub struct Configuration { - // Seed for attester + /// Seed for generating the attester's DID (Decentralized Identifier). #[clap(env)] attester_did_seed: String, - // Seed for payer + + /// Seed for the payer, responsible for transaction fees. #[clap(env)] payer_seed: String, - // Seed for attestation key + + /// Seed for generating the attester's attestation key. #[clap(env)] attester_attestation_seed: String, - // Websocket address of network + + /// Websocket address of the blockchain network. #[clap(env)] pub wss_address: String, + + /// Hostname for serving the attestation service. #[clap(env)] pub host_name: String, + + /// URL for the PostgreSQL database. #[clap(env)] pub database_url: String, + + /// Port for serving the attestation service. #[clap(env)] pub port: u16, + + /// Secret key for JWT (JSON Web Tokens) generation and validation. #[clap(env)] pub jwt_secret: String, + + /// Path to the front-end web application. #[clap(env)] pub front_end_path: String, } impl Configuration { + /// Get the credential signer based on the attester's attestation key seed. + /// Returns a `Result` containing the `PairSigner` or an error if the seed is invalid. pub fn get_credential_signer(&self) -> Result, SecretStringError> { let pair = Pair::from_string_with_seed(&self.attester_attestation_seed, None)?.0; Ok(PairSigner::new(pair)) } + /// Get the payer signer based on the payer's seed. + /// Returns a `Result` containing the `PairSigner` or an error if the seed is invalid. pub fn get_payer_signer(&self) -> Result, SecretStringError> { let pair = Pair::from_string_with_seed(&self.payer_seed, None)?.0; Ok(PairSigner::new(pair)) } + /// Get an online client for interacting with the blockchain network based on the provided WebSocket address. + /// Returns a `Result` containing the `OnlineClient` or an error if the connection fails. pub async fn get_client(&self) -> Result, subxt::Error> { Ok(OnlineClient::::from_url(&self.wss_address).await?) } + /// Get the DID (Decentralized Identifier) for the attester based on the attester's DID seed. + /// Returns a `Result` containing the `AccountId32` representing the DID or an error if the seed is invalid. pub fn get_did(&self) -> Result { let pair = Pair::from_string_with_seed(&self.attester_did_seed, None)?.0; Ok(pair.public().into()) From f5e909aacf4f3c2809ac3d8b8bc69181ef424027 Mon Sep 17 00:00:00 2001 From: Adel Golghalyani Date: Fri, 3 Nov 2023 13:40:24 +0100 Subject: [PATCH 2/5] comments for error --- src/error.rs | 26 +++++++++++++++++++++----- src/main.rs | 12 ++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/error.rs b/src/error.rs index d3e1f9d..6298cd9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,39 +3,55 @@ use serde_json::error::Category; use subxt::ext::sp_core::crypto::SecretStringError; use thiserror::Error; +/// The `AppError` enum represents various error types that can occur within the application. +/// It is used to handle and report different error conditions with user-friendly error messages. #[derive(Debug, Error)] pub enum AppError { + /// Represents a database-related error with an associated `sqlx::Error`. #[error("Database error: {0}")] Database(#[from] sqlx::Error), + + /// Represents a blockchain-related error with an associated `subxt::Error`. #[error("Blockchain error: {0}")] Subxt(#[from] subxt::Error), + + /// Represents a server-related error with an associated `actix_web::Error`. #[error("Server error: {0}")] ActixWeb(#[from] actix_web::Error), + + /// Represents a signature-related error with an associated `SecretStringError`. #[error("Signature error: {0}")] Secret(#[from] SecretStringError), + + /// Represents a JSON-related error with an associated `serde_json::Error`. #[error("JSON error: {0}")] Json(#[from] serde_json::Error), + + /// Represents a hexadecimal encoding/decoding error with an associated `hex::FromHexError`. #[error("Hex error: {0}")] Hex(#[from] hex::FromHexError), } -// Is thread safe. No data races or similar can happen. +/// The `AppError` is designed to be thread-safe. It ensures there are no data races or similar issues. unsafe impl Send for AppError {} unsafe impl Sync for AppError {} +/// The `AppError` implements the `actix_web::error::ResponseError` trait, allowing it to provide customized error responses. impl actix_web::error::ResponseError for AppError { + /// Generates an HTTP response body with the error message. fn error_response(&self) -> HttpResponse { HttpResponse::build(self.status_code()).body(self.to_string()) } + /// Determines the HTTP status code based on the specific error type. fn status_code(&self) -> StatusCode { match self { AppError::Database(sqlx::Error::RowNotFound) => StatusCode::NOT_FOUND, - AppError::Hex(hex::FromHexError::InvalidHexCharacter { .. }) => StatusCode::BAD_REQUEST, - AppError::Hex(hex::FromHexError::InvalidStringLength) => StatusCode::BAD_REQUEST, + AppError::Hex(hex::FromHexError::InvalidHexCharacter { .. }) + | AppError::Hex(hex::FromHexError::InvalidStringLength) => StatusCode::BAD_REQUEST, + AppError::Json(e) => match e.classify() { - Category::Data => StatusCode::BAD_REQUEST, - Category::Syntax => StatusCode::BAD_REQUEST, + Category::Data | Category::Syntax => StatusCode::BAD_REQUEST, _ => StatusCode::INTERNAL_SERVER_ERROR, }, _ => StatusCode::INTERNAL_SERVER_ERROR, diff --git a/src/main.rs b/src/main.rs index 4431865..39bb186 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,12 +17,16 @@ use sqlx::{Pool, Postgres}; use crate::routes::get_attestation_request_scope; +/// The `AppState` struct represents the application state and holds configuration and database-related information. #[derive(Clone)] pub struct AppState { pub config: Configuration, pub db_executor: Pool, } +/// The `JWTPayload` struct represents the payload of a JSON Web Token (JWT). +/// It is deserialized from a JWT and contains various claims like subject (`sub`), expiration (`exp`), etc. +/// This struct is used during JWT validation. #[allow(dead_code)] #[derive(serde::Deserialize)] struct JWTPayload { @@ -36,12 +40,17 @@ struct JWTPayload { nonce: String, } +/// The `User` struct represents a user in the application. +/// It holds user-specific information such as the user DID and whether the user is an admin. #[derive(Clone)] pub struct User { pub id: String, pub is_admin: bool, } +/// The `jwt_validator` function is used as a middleware for JWT (JSON Web Token) authentication. +/// It validates JWTs provided in the request's `Authorization` header, extracts user information, and sets it in the request's extensions. +/// This function is used to authenticate and authorize users. async fn jwt_validator( req: ServiceRequest, credentials: BearerAuth, @@ -88,6 +97,9 @@ async fn jwt_validator( Ok(request) } +/// The `main` function is the entry point for the application. +/// It initializes the application, sets up routes, middleware, and runs the HTTP server. +/// The application's configuration, database connection, and other settings are configured here. #[actix_web::main] async fn main() -> std::io::Result<()> { env_logger::init(); From d2550a0f7610a73a5eb1cb64a0da99b24b768772 Mon Sep 17 00:00:00 2001 From: Adel Golghalyani Date: Fri, 3 Nov 2023 13:45:11 +0100 Subject: [PATCH 3/5] comments for routes --- src/routes.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/routes.rs b/src/routes.rs index 08c73e3..36ab4d1 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -22,6 +22,17 @@ use crate::{ AppState, User, }; +/// Get attestation information by ID. +/// This endpoint allows users to retrieve attestation information by its unique ID. +/// +/// # Parameters +/// - `attestation_request_id`: A unique UUID identifier for the attestation request. +/// - `user`: Information about the requesting user. +/// - `state`: Application state data. +/// +/// # Returns +/// - If the user is authorized to view the attestation, the endpoint responds with a JSON representation of the attestation. +/// - If the user is not authorized, it returns an error response. #[get("/{attestation_request_id}")] async fn get_attestation( path: web::Path, @@ -34,6 +45,17 @@ async fn get_attestation( Ok(HttpResponse::Ok().json(serde_json::to_value(&attestation)?)) } +/// Get a list of attestation requests. +/// This endpoint allows users to retrieve a list of attestation requests based on pagination parameters. +/// +/// # Parameters +/// - `state`: Application state data. +/// - `user`: Information about the requesting user. +/// - `pagination_query`: Query parameters for pagination. +/// +/// # Returns +/// - If the user is authorized to view the attestation requests, the endpoint responds with a JSON list of attestation requests and includes a `Content-Range` header. +/// - If the user is not authorized, it returns an error response. #[get("")] async fn get_attestations( state: web::Data, @@ -53,6 +75,17 @@ async fn get_attestations( .json(response)) } +/// Delete an attestation request by ID. +/// This endpoint allows users to delete an attestation request by its unique ID. +/// +/// # Parameters +/// - `attestation_request_id`: A unique UUID identifier for the attestation request. +/// - `user`: Information about the requesting user. +/// - `state`: Application state data. +/// +/// # Returns +/// - If the user is authorized to delete the attestation, the endpoint responds with a success message. +/// - If the user is not authorized, it returns an error response 401. #[delete("/{attestation_request_id}")] async fn delete_attestation( path: web::Path, @@ -66,6 +99,15 @@ async fn delete_attestation( Ok(HttpResponse::Ok().json("ok")) } +/// Create a new attestation request. +/// This endpoint allows users to create a new attestation request. +/// +/// # Parameters +/// - `body`: JSON data representing the credential for the attestation request. +/// - `state`: Application state data. +/// +/// # Returns +/// - If the attestation request is successfully created, the endpoint responds with a JSON representation of the attestation request. #[post("")] async fn post_attestation( body: web::Json, @@ -77,6 +119,17 @@ async fn post_attestation( Ok(HttpResponse::Ok().json(attestation)) } +/// Approve an attestation request. +/// This endpoint allows administrators to approve an attestation request, which triggers the creation of a new claim. +/// +/// # Parameters +/// - `attestation_request_id`: A unique UUID identifier for the attestation request. +/// - `user`: Information about the requesting user. +/// - `state`: Application state data. +/// +/// # Returns +/// - If the user is an administrator and the approval process is successful, the endpoint responds with a success message. +/// - If the user is not authorized or if there are errors in the approval process, it returns an error response. #[put("/{attestation_request_id}/approve")] async fn approve_attestation( path: web::Path, @@ -139,6 +192,17 @@ async fn approve_attestation( Ok(HttpResponse::Ok().json("ok")) } +/// Revoke an attestation request. +/// This endpoint allows users to revoke a previously approved attestation request. +/// +/// # Parameters +/// - `attestation_request_id`: A unique UUID identifier for the attestation request. +/// - `user`: Information about the requesting user. +/// - `state`: Application state data. +/// +/// # Returns +/// - If the user is authorized and the revocation process is successful, the endpoint responds with a success message. +/// - If the user is not authorized or if there are errors in the revocation process, it returns an error response. #[put("/{attestation_request_id}/revoke")] async fn revoke_attestation( path: web::Path, @@ -193,6 +257,18 @@ async fn revoke_attestation( Ok(HttpResponse::Ok().json("ok")) } +/// Update an attestation request. +/// This endpoint allows users to update an existing attestation request. +/// +/// # Parameters +/// - `attestation_request_id`: A unique UUID identifier for the attestation request to be updated. +/// - `state`: Application state data. +/// - `user`: Information about the requesting user. +/// - `body`: JSON data representing the updated credential for the attestation request. +/// +/// # Returns +/// - If the user is authorized to update the attestation and the update process is successful, the endpoint responds with a JSON representation of the updated attestation request. +/// - If the user is not authorized or if there are errors in the update process, it returns an error response. #[put("/{attestation_request_id}")] async fn update_attestation( path: web::Path, @@ -212,6 +288,15 @@ async fn update_attestation( Ok(HttpResponse::Ok().json(serde_json::to_value(&attestation)?)) } +/// Get attestation key performance indicators (KPIs). +/// This endpoint allows users to retrieve key performance indicators related to attestation requests. +/// +/// # Parameters +/// - `state`: Application state data. +/// +/// # Returns +/// - If the request is successful, the endpoint responds with a JSON representation of attestation-related KPIs. +/// - If there are errors in the retrieval process, it returns an error response. #[get("/metric/kpis")] async fn get_attestation_kpis(state: web::Data) -> Result { let kpis = attestation_requests_kpis(&state.db_executor).await?; From e5b24e5ac558c3b06048937ca5a37c3b507b1b43 Mon Sep 17 00:00:00 2001 From: Adel Golghalyani Date: Fri, 3 Nov 2023 13:46:35 +0100 Subject: [PATCH 4/5] comments for routes --- src/routes.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/routes.rs b/src/routes.rs index 36ab4d1..3fef15c 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -303,6 +303,12 @@ async fn get_attestation_kpis(state: web::Data) -> Result Scope { web::scope("/api/v1/attestation_request") .service(approve_attestation) From 9b529272f1b72da0baa70228ee9bbe160754b927 Mon Sep 17 00:00:00 2001 From: Adel Golghalyani Date: Fri, 3 Nov 2023 13:57:28 +0100 Subject: [PATCH 5/5] comments for utils --- src/utils.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/utils.rs b/src/utils.rs index 174d1d6..feaf358 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -11,6 +11,13 @@ use crate::{ User, }; +/// Get the current block number from the blockchain. +/// +/// # Arguments +/// - `config`: A reference to the `Configuration` struct containing blockchain configuration. +/// +/// # Returns +/// A `Result` containing the current block number or an error. pub async fn get_current_block(config: &Configuration) -> Result { let api = config.get_client().await?; let block_number = api @@ -25,6 +32,13 @@ pub async fn get_current_block(config: &Configuration) -> Result Result { let api = config.get_client().await?; let did_doc_addr = kilt::storage().did().did(&config.get_did()?); @@ -39,6 +53,14 @@ pub async fn get_next_tx_counter(config: &Configuration) -> Result, attestatations: &Vec, @@ -68,6 +98,15 @@ pub fn is_user_allowed_to_see_data( } } +/// Check if a user is allowed to update certain attestation data. +/// +/// # Arguments +/// - `user`: The user data. +/// - `attestation_id`: The ID of the attestation request. +/// - `db_executor`: A reference to the database executor. +/// +/// # Returns +/// An `Result` indicating whether the user is allowed to update the data or an error. pub async fn is_user_allowed_to_update_data( user: ReqData, attestation_id: &Uuid, @@ -84,6 +123,13 @@ pub async fn is_user_allowed_to_update_data( } } +/// Check if a user is an administrator. +/// +/// # Arguments +/// - `user`: The user data. +/// +/// # Returns +/// An `Result` indicating whether the user is an administrator or an error. pub fn is_user_admin(user: ReqData) -> Result<(), actix_web::Error> { if !user.is_admin { Err(actix_web::error::ErrorUnauthorized(