Skip to content

Commit dae5282

Browse files
committed
feat:开发进展
1 parent f5b2e0f commit dae5282

File tree

16 files changed

+310
-98
lines changed

16 files changed

+310
-98
lines changed

crates/capsula-pki-server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ serde = { version = "1", features = ["derive"] }
2525
serde_json = "1"
2626
validator = { version = "0.20", features = ["derive"] }
2727
thiserror = "2"
28+
surrealdb = { version = "2.3.8" }
2829

2930
# API documentation
3031
utoipa = { version = "5", features = ["axum_extras", "uuid", "chrono"] }
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
pub mod user;
2+
3+
use std::sync::LazyLock;
4+
5+
use serde::Deserialize;
6+
use surrealdb::{
7+
engine::remote::ws::{Client, Ws},
8+
opt::auth::Root,
9+
Surreal,
10+
};
11+
12+
use crate::{db::user::create_users_table, error::Result};
13+
14+
/// Struct representing the Surrealdb configuration parameters.
15+
#[derive(Debug, Deserialize)]
16+
pub struct SurrealdbCfg {
17+
pub host: String,
18+
pub port: u16,
19+
pub username: String,
20+
pub password: String,
21+
pub namespace: String,
22+
pub database: String,
23+
}
24+
25+
static DB: LazyLock<Surreal<Client>> = LazyLock::new(Surreal::init);
26+
27+
pub async fn init_db(cfg: SurrealdbCfg) -> Result<()> {
28+
let addr = format!("{}:{}", cfg.host, cfg.port);
29+
DB.connect::<Ws>(addr).await?;
30+
DB.signin(Root {
31+
username: &cfg.username,
32+
password: &cfg.password,
33+
})
34+
.await?;
35+
DB.use_ns(cfg.namespace).use_db(cfg.database).await?;
36+
Ok(())
37+
}
38+
39+
pub fn get_db() -> &'static Surreal<Client> {
40+
&DB
41+
}
42+
43+
pub async fn create_tables() -> Result<()> {
44+
create_users_table().await?;
45+
Ok(())
46+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use crate::{
2+
db::get_db,
3+
error::Result,
4+
models::user::{User, UserInput},
5+
};
6+
7+
pub async fn create_users_table() -> Result<()> {
8+
let query = r#"
9+
DEFINE TABLE IF NOT EXISTS users SCHEMALESS PERMISSIONS FULL;
10+
11+
DEFINE FIELD IF NOT EXISTS uuid ON TABLE users TYPE string READONLY;
12+
DEFINE FIELD IF NOT EXISTS username ON TABLE users TYPE string READONLY;
13+
DEFINE FIELD IF NOT EXISTS password ON TABLE users TYPE string;
14+
DEFINE FIELD IF NOT EXISTS email ON TABLE users TYPE option<string>;
15+
DEFINE FIELD IF NOT EXISTS email_verified ON TABLE users TYPE bool VALUE false;
16+
DEFINE FIELD IF NOT EXISTS role ON TABLE users TYPE string;
17+
DEFINE FIELD IF NOT EXISTS disabled ON TABLE users TYPE bool VALUE false;
18+
DEFINE FIELD IF NOT EXISTS mfa_enabled ON TABLE users TYPE bool VALUE false;
19+
DEFINE FIELD IF NOT EXISTS mfa_secret ON TABLE users TYPE option<string>;
20+
DEFINE FIELD IF NOT EXISTS created_at ON TABLE users TYPE datetime VALUE time::now() READONLY;
21+
DEFINE FIELD IF NOT EXISTS updated_at ON TABLE users TYPE datetime VALUE time::now();
22+
23+
DEFINE INDEX IF NOT EXISTS unique_uuid ON TABLE users FIELDS uuid UNIQUE;
24+
DEFINE INDEX IF NOT EXISTS unique_username ON TABLE users FIELDS username UNIQUE;
25+
DEFINE INDEX IF NOT EXISTS unique_email ON TABLE users FIELDS email UNIQUE;
26+
"#;
27+
28+
let db = get_db();
29+
db.query(query).await?;
30+
31+
Ok(())
32+
}
33+
34+
pub async fn create_user(input: UserInput) -> Result<()> {
35+
let db = get_db();
36+
let _r: Option<User> = db.create(("users", &input.uuid)).content(input).await?;
37+
Ok(())
38+
}
39+
40+
pub async fn get_user_by_name(username: &str) -> Result<Option<User>> {
41+
let db = get_db();
42+
let query = "SELECT * FROM users WHERE username = $username";
43+
let result: Option<User> = db
44+
.query(query)
45+
.bind(("username", username.to_string()))
46+
.await?
47+
.take(0)?;
48+
Ok(result)
49+
}
50+
51+
pub async fn get_user_by_id(user_id: &str) -> Result<Option<User>> {
52+
let db = get_db();
53+
let r: Option<User> = db.select(("users", user_id)).await?;
54+
Ok(r)
55+
}

crates/capsula-pki-server/src/error/mod.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
pub mod error_code;
22

33
use axum::{
4-
Json,
54
http::StatusCode,
65
response::{IntoResponse, Response},
6+
Json,
77
};
88
use serde_json::json;
99
use thiserror::Error;
@@ -19,19 +19,19 @@ pub enum AppError {
1919
#[allow(clippy::enum_variant_names)]
2020
ValidationError(#[from] validator::ValidationErrors),
2121

22+
#[error("db error: {0}")]
23+
DbError(#[from] surrealdb::Error),
24+
2225
#[error("not found: {0}")]
2326
NotFound(String),
24-
27+
2528
#[error("PKI error: {0}")]
2629
PkiError(String),
27-
30+
2831
#[error("internal error: {0}")]
2932
Internal(String),
3033
}
3134

32-
// Keep the old Error type as alias for backward compatibility
33-
pub type Error = AppError;
34-
3535
impl IntoResponse for AppError {
3636
fn into_response(self) -> Response {
3737
let (status, error_message) = match self {
@@ -40,6 +40,7 @@ impl IntoResponse for AppError {
4040
AppError::NotFound(ref e) => (StatusCode::NOT_FOUND, e.to_string()),
4141
AppError::PkiError(ref e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
4242
AppError::Internal(ref e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
43+
AppError::DbError(ref e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
4344
};
4445

4546
let body = Json(json!({

crates/capsula-pki-server/src/handlers/ca.rs

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@
33
use axum::{
44
http::StatusCode,
55
response::Json,
6+
routing::{get, post},
7+
Router,
68
};
7-
use utoipa_axum::router::OpenApiRouter;
8-
use utoipa_axum::routes;
9-
10-
use crate::models::ca::{CaInfo, CaInitRequest, CaStatus};
11-
use crate::error::AppError;
9+
use capsula_pki::keystore::{
10+
FileSystemBackend, KeyGenerationConfig, KeyType, KeyUsage, KeystoreManager,
11+
};
12+
use utoipa_axum::{router::OpenApiRouter, routes};
1213

13-
use capsula_pki::keystore::{KeystoreManager, KeyGenerationConfig, KeyType, KeyUsage, FileSystemBackend};
14+
use crate::{
15+
error::AppError,
16+
models::ca::{CaInfo, CaInitRequest, CaStatus},
17+
};
1418

1519
/// Get CA status and information
1620
#[utoipa::path(
@@ -24,15 +28,16 @@ use capsula_pki::keystore::{KeystoreManager, KeyGenerationConfig, KeyType, KeyUs
2428
)]
2529
pub async fn get_ca_status() -> Result<Json<CaStatus>, AppError> {
2630
tracing::info!("Getting CA status");
27-
31+
2832
// TODO: Implement CA status check
2933
// 1. Check if CA is initialized
3034
// 2. Get CA certificate information
3135
// 3. Get statistics from storage
32-
36+
3337
// Placeholder response
3438
let ca_info = CaInfo {
35-
ca_certificate_pem: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----".to_string(),
39+
ca_certificate_pem: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
40+
.to_string(),
3641
subject: "CN=Capsula Root CA, O=Capsula PKI, C=US".to_string(),
3742
serial_number: "1".to_string(),
3843
not_before: chrono::Utc::now() - chrono::Duration::days(1),
@@ -41,15 +46,15 @@ pub async fn get_ca_status() -> Result<Json<CaStatus>, AppError> {
4146
key_size: Some(4096),
4247
created_at: chrono::Utc::now() - chrono::Duration::days(1),
4348
};
44-
49+
4550
let status = CaStatus {
4651
initialized: true,
4752
ca_info: Some(ca_info),
4853
certificates_issued: 0,
4954
active_certificates: 0,
5055
revoked_certificates: 0,
5156
};
52-
57+
5358
Ok(Json(status))
5459
}
5560

@@ -66,12 +71,13 @@ pub async fn get_ca_status() -> Result<Json<CaStatus>, AppError> {
6671
)]
6772
pub async fn get_ca_certificate() -> Result<Json<CaInfo>, AppError> {
6873
tracing::info!("Getting CA certificate");
69-
74+
7075
// TODO: Implement CA certificate retrieval
71-
76+
7277
// Placeholder response
7378
let ca_info = CaInfo {
74-
ca_certificate_pem: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----".to_string(),
79+
ca_certificate_pem: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
80+
.to_string(),
7581
subject: "CN=Capsula Root CA, O=Capsula PKI, C=US".to_string(),
7682
serial_number: "1".to_string(),
7783
not_before: chrono::Utc::now() - chrono::Duration::days(1),
@@ -80,14 +86,14 @@ pub async fn get_ca_certificate() -> Result<Json<CaInfo>, AppError> {
8086
key_size: Some(4096),
8187
created_at: chrono::Utc::now() - chrono::Duration::days(1),
8288
};
83-
89+
8490
Ok(Json(ca_info))
8591
}
8692

8793
/// Initialize Certificate Authority
8894
#[utoipa::path(
8995
post,
90-
path = "/api/v1/ca/init",
96+
path = "/ca/init",
9197
request_body = CaInitRequest,
9298
responses(
9399
(status = 201, description = "CA initialized successfully", body = CaInfo),
@@ -100,7 +106,7 @@ pub async fn initialize_ca(
100106
Json(request): Json<CaInitRequest>,
101107
) -> Result<(StatusCode, Json<CaInfo>), AppError> {
102108
tracing::info!("Initializing CA with CN: {}", request.common_name);
103-
109+
104110
// Simple CA initialization
105111
match simple_initialize_ca(&request).await {
106112
Ok(ca_info) => Ok((StatusCode::CREATED, Json(ca_info))),
@@ -112,46 +118,51 @@ pub async fn initialize_ca(
112118
}
113119

114120
/// Simple CA initialization implementation
115-
async fn simple_initialize_ca(request: &CaInitRequest) -> Result<CaInfo, Box<dyn std::error::Error>> {
121+
async fn simple_initialize_ca(
122+
request: &CaInitRequest,
123+
) -> Result<CaInfo, Box<dyn std::error::Error>> {
116124
tracing::info!("Creating CA with basic PKI integration");
117-
125+
118126
// 1. Create keystore manager
119127
let storage_backend = Box::new(FileSystemBackend::new("./pki_data/keys")?);
120128
let mut keystore_manager = KeystoreManager::new(storage_backend);
121-
129+
122130
// 2. Generate CA key pair
123131
let key_type = match request.key_algorithm.as_str() {
124132
"RSA" => KeyType::RSA(request.key_size.unwrap_or(2048)),
125133
"Ed25519" => KeyType::Ed25519,
126134
_ => KeyType::Ed25519, // Default to Ed25519
127135
};
128-
136+
129137
let config = KeyGenerationConfig {
130138
key_type,
131139
usages: vec![KeyUsage::CertificateSigning, KeyUsage::CRLSigning],
132140
use_hsm: false,
133141
exportable: false, // CA key should not be exportable
134142
label: Some("CA Root Key".to_string()),
135143
};
136-
144+
137145
let (ca_key_id, _ca_key) = keystore_manager.generate_key(config)?;
138146
tracing::info!("Generated CA key with ID: {}", ca_key_id);
139-
147+
140148
// 3. Create CA info (simplified without actual certificate generation)
141149
let ca_info = CaInfo {
142150
ca_certificate_pem: format!(
143-
"-----BEGIN CERTIFICATE-----\nTEMPORARY_CA_CERT_FOR_KEY_{}\n-----END CERTIFICATE-----",
151+
"-----BEGIN CERTIFICATE-----\nTEMPORARY_CA_CERT_FOR_KEY_{}\n-----END CERTIFICATE-----",
144152
ca_key_id
145153
),
146-
subject: format!("CN={}, O={}, C={}", request.common_name, request.organization, request.country),
154+
subject: format!(
155+
"CN={}, O={}, C={}",
156+
request.common_name, request.organization, request.country
157+
),
147158
serial_number: "1".to_string(),
148159
not_before: chrono::Utc::now(),
149160
not_after: chrono::Utc::now() + chrono::Duration::days(request.validity_days as i64),
150161
key_algorithm: request.key_algorithm.clone(),
151162
key_size: request.key_size,
152163
created_at: chrono::Utc::now(),
153164
};
154-
165+
155166
tracing::info!("CA initialized successfully");
156167
Ok(ca_info)
157168
}
@@ -168,11 +179,3 @@ async fn simple_initialize_ca(request: &CaInitRequest) -> Result<CaInfo, Box<dyn
168179
pub async fn health_check() -> StatusCode {
169180
StatusCode::OK
170181
}
171-
172-
pub fn create_router() -> OpenApiRouter {
173-
OpenApiRouter::new()
174-
.routes(routes!(get_ca_status))
175-
.routes(routes!(get_ca_certificate))
176-
.routes(routes!(initialize_ca))
177-
.routes(routes!(health_check))
178-
}

crates/capsula-pki-server/src/handlers/certificate.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use axum::{
44
extract::{Path, Query},
55
http::StatusCode,
66
response::Json,
7+
routing::{get, post},
8+
Router,
79
};
810
use utoipa_axum::{router::OpenApiRouter, routes};
911
use uuid::Uuid;
@@ -164,11 +166,3 @@ pub async fn revoke_certificate(
164166

165167
Ok(StatusCode::OK)
166168
}
167-
168-
pub fn create_router() -> OpenApiRouter {
169-
OpenApiRouter::new()
170-
.routes(routes!(create_certificate))
171-
.routes(routes!(get_certificate))
172-
.routes(routes!(list_certificates))
173-
.routes(routes!(revoke_certificate))
174-
}

crates/capsula-pki-server/src/handlers/simple_ca.rs renamed to crates/capsula-pki-server/src/handlers/health.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use axum::{http::StatusCode, response::Json, routing::get, Router};
1+
use axum::response::Json;
22
use serde_json::json;
33

44
pub async fn health() -> Json<serde_json::Value> {
@@ -14,9 +14,3 @@ pub async fn ca_status() -> Json<serde_json::Value> {
1414
"message": "Simple PKI server working"
1515
}))
1616
}
17-
18-
pub fn create_simple_router() -> Router {
19-
Router::new()
20-
.route("/simple-health", get(health))
21-
.route("/simple-ca-status", get(ca_status))
22-
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
pub mod certificate;
21
pub mod ca;
3-
pub mod simple_ca;
2+
pub mod certificate;
3+
pub mod health;

0 commit comments

Comments
 (0)