summaryrefslogtreecommitdiffstats
path: root/adenosine-pds/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'adenosine-pds/src/lib.rs')
-rw-r--r--adenosine-pds/src/lib.rs118
1 files changed, 97 insertions, 21 deletions
diff --git a/adenosine-pds/src/lib.rs b/adenosine-pds/src/lib.rs
index 4ea2f7b..0d73881 100644
--- a/adenosine-pds/src/lib.rs
+++ b/adenosine-pds/src/lib.rs
@@ -1,6 +1,6 @@
use anyhow::{anyhow, Result};
use libipld::Ipld;
-use log::{error, info, warn};
+use log::{debug, error, info, warn};
use rouille::{router, Request, Response};
use serde_json::{json, Value};
use std::fmt;
@@ -23,16 +23,6 @@ pub use models::*;
pub use repo::{RepoCommit, RepoStore};
pub use ucan_p256::P256KeyMaterial;
-#[allow(non_snake_case)]
-#[derive(Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
-struct AccountRequest {
- email: String,
- username: String,
- password: String,
- inviteCode: Option<String>,
- recoveryKey: Option<String>,
-}
-
struct AtpService {
pub repo: RepoStore,
pub atp_db: AtpDatabase,
@@ -77,14 +67,16 @@ fn xrpc_wrap<S: serde::Serialize>(resp: Result<S>) -> Response {
}
}
-pub fn run_server(port: u16, blockstore_db_path: &PathBuf, atp_db_path: &PathBuf) -> Result<()> {
- // TODO: some static files? https://github.com/tomaka/rouille/blob/master/examples/static-files.rs
-
+pub fn run_server(
+ port: u16,
+ blockstore_db_path: &PathBuf,
+ atp_db_path: &PathBuf,
+ keypair: KeyPair,
+) -> Result<()> {
let srv = Mutex::new(AtpService {
repo: RepoStore::open(blockstore_db_path)?,
atp_db: AtpDatabase::open(atp_db_path)?,
- // XXX: reuse a keypair
- pds_keypair: KeyPair::new_random(),
+ pds_keypair: keypair,
pds_public_url: format!("http://localhost:{}", port).to_string(),
});
@@ -99,6 +91,9 @@ pub fn run_server(port: u16, blockstore_db_path: &PathBuf, atp_db_path: &PathBuf
elap
);
};
+
+ // TODO: robots.txt
+ // TODO: some static files? https://github.com/tomaka/rouille/blob/master/examples/static-files.rs
rouille::start_server(format!("localhost:{}", port), move |request| {
rouille::log_custom(request, log_ok, log_err, || {
router!(request,
@@ -142,6 +137,21 @@ fn xrpc_required_param(request: &Request, key: &str) -> Result<String> {
)))?)
}
+/// Returns DID of validated user
+fn xrpc_check_auth_header(srv: &mut AtpService, request: &Request) -> Result<String> {
+ let header = request
+ .header("Authorization")
+ .ok_or(XrpcError::Forbidden(format!("require auth header")))?;
+ if !header.starts_with("Bearer ") {
+ Err(XrpcError::Forbidden(format!("require bearer token")))?;
+ }
+ let jwt = header.split(" ").nth(1).unwrap();
+ match srv.atp_db.check_auth_token(&jwt)? {
+ Some(did) => Ok(did),
+ None => Err(XrpcError::Forbidden(format!("session token not found")))?,
+ }
+}
+
fn xrpc_get_handler(
srv: &Mutex<AtpService>,
method: &str,
@@ -175,6 +185,45 @@ fn xrpc_get_handler(
.map(|v| json!({ "root": v }))
.ok_or(XrpcError::NotFound(format!("no repository found for DID: {}", did)).into())
}
+ "com.atproto.repoListRecords" => {
+ // TODO: limit, before, after, tid, reverse
+ // TODO: handle non-DID 'user'
+ // TODO: validate 'collection' as an NSID
+ // TODO: limit result set size
+ let did = xrpc_required_param(request, "user")?;
+ let collection = xrpc_required_param(request, "collection")?;
+ let mut record_list: Vec<Value> = vec![];
+ let mut srv = srv.lock().expect("service mutex");
+ let full_map = srv.repo.mst_to_map(&did)?;
+ let prefix = format!("/{}/", collection);
+ for (mst_key, cid) in full_map.iter() {
+ if mst_key.starts_with(&prefix) {
+ let record = srv.repo.get_ipld(cid)?;
+ record_list.push(json!({
+ "uri": format!("at://{}{}", did, mst_key),
+ "cid": cid,
+ "value": ipld_into_json_value(record),
+ }));
+ }
+ }
+ Ok(json!({ "records": record_list }))
+ }
+ "com.atproto.repoDescribe" => {
+ let did = xrpc_required_param(request, "user")?;
+ // TODO: resolve username?
+ let username = did.clone();
+ let mut srv = srv.lock().expect("service mutex");
+ let did_doc = srv.atp_db.get_did_doc(&did)?;
+ let collections: Vec<String> = srv.repo.collections(&did)?;
+ let desc = RepoDescribe {
+ name: username,
+ did: did,
+ didDoc: did_doc,
+ collections: collections,
+ nameIsCorrect: true,
+ };
+ Ok(json!(desc))
+ }
_ => Err(anyhow!(XrpcError::NotFound(format!(
"XRPC endpoint handler not found: {}",
method
@@ -186,12 +235,9 @@ fn xrpc_post_handler(
srv: &Mutex<AtpService>,
method: &str,
request: &Request,
-) -> Result<impl serde::Serialize> {
+) -> Result<serde_json::Value> {
match method {
"com.atproto.createAccount" => {
- // TODO: generate did:plc, and insert an empty record/pointer to repo
- info!("creating new account");
-
// validate account request
let req: AccountRequest = rouille::input::json_input(request)
.map_err(|e| XrpcError::BadRequest(format!("failed to parse JSON body: {}", e)))?;
@@ -205,6 +251,8 @@ fn xrpc_post_handler(
)))?;
};
+ debug!("trying to create new account: {}", &req.username);
+
// generate DID
let create_op = did::CreateOp::new(
req.username.clone(),
@@ -241,7 +289,35 @@ fn xrpc_post_handler(
let sess = srv
.atp_db
.create_session(&req.username, &req.password, &keypair)?;
- Ok(sess)
+ Ok(json!(sess))
+ }
+ "com.atproto.createSession" => {
+ let req: AccountRequest = rouille::input::json_input(request)
+ .map_err(|e| XrpcError::BadRequest(format!("failed to parse JSON body: {}", e)))?;
+ let mut srv = srv.lock().unwrap();
+ let keypair = srv.pds_keypair.clone();
+ Ok(json!(srv.atp_db.create_session(
+ &req.username,
+ &req.password,
+ &keypair
+ )?))
+ }
+ "com.atproto.deleteSession" => {
+ let mut srv = srv.lock().unwrap();
+ let _did = xrpc_check_auth_header(&mut srv, request)?;
+ let header = request
+ .header("Authorization")
+ .ok_or(XrpcError::Forbidden(format!("require auth header")))?;
+ if !header.starts_with("Bearer ") {
+ Err(XrpcError::Forbidden(format!("require bearer token")))?;
+ }
+ let jwt = header.split(" ").nth(1).expect("JWT in header");
+ if !srv.atp_db.delete_session(&jwt)? {
+ Err(anyhow!(
+ "session token not found, even after using for auth"
+ ))?
+ };
+ Ok(json!({}))
}
_ => Err(anyhow!(XrpcError::NotFound(format!(
"XRPC endpoint handler not found: {}",