Skip to content

Commit 90b33dd

Browse files
committed
Removed any possible panics. Moved model into the rsky-lexicon
1 parent 4ba3fba commit 90b33dd

File tree

3 files changed

+136
-35
lines changed

3 files changed

+136
-35
lines changed

rsky-lexicon/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rsky-lexicon"
3-
version = "0.2.5"
3+
version = "0.2.6"
44
edition = "2021"
55
publish = true
66
description = "Bluesky API library"

rsky-lexicon/src/com/atproto/identity.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,9 @@ pub struct SignPlcOperationRequest {
2424
pub verification_methods: Option<BTreeMap<String, String>>,
2525
pub services: Option<JsonValue>,
2626
}
27+
28+
#[derive(Clone, Debug, Serialize, Deserialize)]
29+
#[serde(rename_all = "camelCase")]
30+
pub struct SubmitPlcOperationRequest {
31+
pub operation: JsonValue,
32+
}

rsky-pds/src/apis/com/atproto/identity/submit_plc_operation.rs

Lines changed: 129 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,112 @@
11
use crate::account_manager::helpers::account::AvailabilityFlags;
22
use crate::account_manager::AccountManager;
3-
use crate::apis::com::atproto::server::get_keys_from_private_key_str;
43
use crate::apis::ApiError;
54
use crate::auth_verifier::AccessStandard;
65
use crate::config::ServerConfig;
76
use crate::plc::types::{OpOrTombstone, Operation};
87
use crate::{plc, SharedIdResolver, SharedSequencer};
98
use rocket::serde::json::Json;
109
use rocket::State;
11-
use rsky_common::env::env_str;
1210
use rsky_crypto::utils::encode_did_key;
11+
use rsky_lexicon::com::atproto::identity::SubmitPlcOperationRequest;
12+
use secp256k1::{Keypair, Secp256k1, SecretKey};
1313
use std::env;
1414

15-
async fn validate_submit_plc_operation_request(
15+
#[tracing::instrument(skip_all)]
16+
fn get_requester_did(auth: &AccessStandard) -> Result<String, ApiError> {
17+
match &auth.access.credentials {
18+
None => {
19+
tracing::error!("Failed to find access credentials");
20+
Err(ApiError::RuntimeError)
21+
}
22+
Some(res) => match &res.did {
23+
None => {
24+
tracing::error!("Failed to find did");
25+
Err(ApiError::RuntimeError)
26+
}
27+
Some(did) => Ok(did.clone()),
28+
},
29+
}
30+
}
31+
32+
#[tracing::instrument(skip_all)]
33+
fn get_public_rotation_key() -> Result<String, ApiError> {
34+
let secp = Secp256k1::new();
35+
let private_rotation_key = match env::var("PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX") {
36+
Ok(res) => res,
37+
Err(error) => {
38+
tracing::error!("Error geting rotation private key\n{error}");
39+
return Err(ApiError::RuntimeError);
40+
}
41+
};
42+
match hex::decode(private_rotation_key.as_bytes()) {
43+
Ok(bytes) => match SecretKey::from_slice(&bytes) {
44+
Ok(secret_key) => {
45+
let rotation_keypair = Keypair::from_secret_key(&secp, &secret_key);
46+
Ok(encode_did_key(&rotation_keypair.public_key()))
47+
}
48+
Err(error) => {
49+
tracing::error!("Error geting rotation secret key from bytes\n{error}");
50+
Err(ApiError::RuntimeError)
51+
}
52+
},
53+
Err(error) => {
54+
tracing::error!("Unable to hex decode rotation key\n{error}");
55+
Err(ApiError::RuntimeError)
56+
}
57+
}
58+
}
59+
60+
#[tracing::instrument(skip_all)]
61+
fn get_public_signing_key() -> Result<String, ApiError> {
62+
let secp = Secp256k1::new();
63+
let private_signing_key = match env::var("PDS_REPO_SIGNING_KEY_K256_PRIVATE_KEY_HEX") {
64+
Ok(res) => res,
65+
Err(error) => {
66+
tracing::error!("Error geting signing private key\n{error}");
67+
return Err(ApiError::RuntimeError);
68+
}
69+
};
70+
match hex::decode(private_signing_key.as_bytes()) {
71+
Ok(bytes) => match SecretKey::from_slice(&bytes) {
72+
Ok(secret_key) => {
73+
let signing_keypair = Keypair::from_secret_key(&secp, &secret_key);
74+
Ok(encode_did_key(&signing_keypair.public_key()))
75+
}
76+
Err(error) => {
77+
tracing::error!("Error geting signing secret key from bytes\n{error}");
78+
Err(ApiError::RuntimeError)
79+
}
80+
},
81+
Err(error) => {
82+
tracing::error!("Unable to hex decode signing key\n{error}");
83+
Err(ApiError::RuntimeError)
84+
}
85+
}
86+
}
87+
88+
#[tracing::instrument(skip_all)]
89+
async fn validate_plc_request(
1690
did: &str,
1791
op: &Operation,
1892
public_endpoint: &str,
1993
) -> Result<(), ApiError> {
20-
let private_key = env::var("PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX").unwrap();
21-
let (_, public_key) = get_keys_from_private_key_str(private_key)?;
22-
let plc_rotation_key = encode_did_key(&public_key);
23-
if !op.rotation_keys.contains(&plc_rotation_key) {
94+
let public_rotation_key = get_public_signing_key()?;
95+
if !op.rotation_keys.contains(&public_rotation_key) {
2496
return Err(ApiError::InvalidRequest(
2597
"Rotation keys do not include server's rotation key".to_string(),
2698
));
2799
}
28100

29-
let private_key = env::var("PDS_REPO_SIGNING_KEY_K256_PRIVATE_KEY_HEX").unwrap();
30-
let (_, public_key) = get_keys_from_private_key_str(private_key)?;
31-
let signing_rotation_key = encode_did_key(&public_key);
101+
let public_signing_key = get_public_signing_key()?;
32102
match op.verification_methods.get("atproto") {
33103
None => {
34104
return Err(ApiError::InvalidRequest(
35105
"Incorrect signing key".to_string(),
36106
))
37107
}
38108
Some(res) => {
39-
if res.clone() != signing_rotation_key {
109+
if res.clone() != public_signing_key {
40110
return Err(ApiError::InvalidRequest(
41111
"Incorrect signing key".to_string(),
42112
));
@@ -102,46 +172,71 @@ async fn validate_submit_plc_operation_request(
102172
Ok(())
103173
}
104174

175+
#[tracing::instrument(skip_all)]
176+
async fn do_plc_operation(plc_url: &str, did: &str, op: Operation) -> Result<(), ApiError> {
177+
let plc_client = plc::Client::new(plc_url.to_string());
178+
match plc_client
179+
.send_operation(&did.to_string(), &OpOrTombstone::Operation(op))
180+
.await
181+
{
182+
Ok(_res) => {
183+
tracing::info!("Successfully sent PLC Update Operation");
184+
Ok(())
185+
}
186+
Err(error) => {
187+
tracing::error!("Failed to update did:plc\n{error}");
188+
Err(ApiError::RuntimeError)
189+
}
190+
}
191+
}
192+
193+
#[tracing::instrument(skip_all)]
194+
fn validate_operation_body(request: SubmitPlcOperationRequest) -> Result<Operation, ApiError> {
195+
match serde_json::from_value::<Operation>(request.operation) {
196+
Ok(op) => {
197+
tracing::debug!("Sucessfully parsed operation body");
198+
Ok(op)
199+
}
200+
Err(error) => {
201+
tracing::error!("Error parsing operation body\n{error}");
202+
Err(ApiError::InvalidRequest("Invalid operation".to_string()))
203+
}
204+
}
205+
}
206+
105207
#[rocket::post(
106208
"/xrpc/com.atproto.identity.submitPlcOperation",
107209
format = "json",
108210
data = "<body>"
109211
)]
110212
#[tracing::instrument(skip_all)]
111213
pub async fn submit_plc_operation(
112-
body: Json<Operation>,
214+
body: Json<SubmitPlcOperationRequest>,
113215
auth: AccessStandard,
114216
sequencer: &State<SharedSequencer>,
115217
id_resolver: &State<SharedIdResolver>,
116218
server_config: &State<ServerConfig>,
117219
) -> Result<(), ApiError> {
118-
let did = auth.access.credentials.unwrap().did.unwrap();
119-
let op = body.into_inner();
120-
let public_endpoint = server_config.service.public_url.as_str();
220+
let did = get_requester_did(&auth)?;
121221

122-
validate_submit_plc_operation_request(did.as_str(), &op, public_endpoint).await?;
222+
//Validate and transform request
223+
let op = validate_operation_body(body.into_inner())?;
123224

124-
let plc_url = env_str("PDS_DID_PLC_URL").unwrap_or("https://plc.directory".to_owned());
125-
let plc_client = plc::Client::new(plc_url);
126-
match plc_client
127-
.send_operation(&did, &OpOrTombstone::Operation(op))
128-
.await
129-
{
130-
Ok(_) => {
131-
tracing::info!("Successfully sent PLC Update Operation");
132-
}
133-
Err(_) => {
134-
tracing::error!("Failed to update did:plc");
135-
return Err(ApiError::RuntimeError);
136-
}
137-
}
138-
let mut sequence_lock = sequencer.sequencer.write().await;
139-
sequence_lock
140-
.sequence_identity_evt(did.clone(), None)
141-
.await?;
225+
//Validate PLC Operation is valid
226+
validate_plc_request(did.as_str(), &op, server_config.service.public_url.as_str()).await?;
227+
228+
//Send PLC Operation to PLC Service
229+
do_plc_operation(server_config.identity.plc_url.as_str(), did.as_str(), op).await?;
230+
231+
//Update Sequencer
232+
let mut seq_lock = sequencer.sequencer.write().await;
233+
seq_lock.sequence_identity_evt(did.clone(), None).await?;
234+
235+
//Refresh DID after PLC update
142236
let mut id_lock = id_resolver.id_resolver.write().await;
143237
if let Err(error) = id_lock.did.ensure_resolve(&did, None).await {
144238
tracing::error!("Failed to fresh did after plc update\n{error}")
145239
};
240+
146241
Ok(())
147242
}

0 commit comments

Comments
 (0)