forest/rpc/methods/
auth.rs1use crate::{
5 KeyStore,
6 auth::*,
7 lotus_json::lotus_json_with_self,
8 rpc::{ApiPaths, Ctx, Permission, RpcMethod, ServerError},
9};
10use anyhow::{Context as _, Result};
11use chrono::Duration;
12use enumflags2::BitFlags;
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15use serde_with::{DurationSeconds, serde_as};
16
17pub enum AuthNew {}
19
20impl AuthNew {
21 pub fn create_token(
22 keystore: &KeyStore,
23 token_exp: Duration,
24 permissions: Vec<String>,
25 ) -> anyhow::Result<String> {
26 let ki = keystore.get(JWT_IDENTIFIER)?;
27 Ok(create_token(permissions, ki.private_key(), token_exp)?)
28 }
29}
30
31impl RpcMethod<2> for AuthNew {
32 const NAME: &'static str = "Filecoin.AuthNew";
33 const N_REQUIRED_PARAMS: usize = 1;
34 const PARAM_NAMES: [&'static str; 2] = ["permissions", "expirationSecs"];
36 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
37 const PERMISSION: Permission = Permission::Admin;
38 const DESCRIPTION: &'static str =
39 "Creates a new JWT authentication token with the given permissions.";
40 type Params = (Vec<String>, Option<i64>);
41 type Ok = Vec<u8>;
42 async fn handle(
43 ctx: Ctx,
44 (permissions, expiration_secs): Self::Params,
45 _: &http::Extensions,
46 ) -> Result<Self::Ok, ServerError> {
47 let ks = ctx.keystore.read();
48 let token_exp = match expiration_secs {
52 Some(secs) => Duration::try_seconds(secs)
53 .with_context(|| format!("expirationSecs out of range: {secs}"))?,
54 None => Duration::days(365 * 100),
55 };
56 let token = Self::create_token(&ks, token_exp, permissions)?;
57 Ok(token.as_bytes().to_vec())
58 }
59}
60
61pub enum AuthVerify {}
62impl RpcMethod<1> for AuthVerify {
63 const NAME: &'static str = "Filecoin.AuthVerify";
64 const PARAM_NAMES: [&'static str; 1] = ["token"];
65 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
66 const PERMISSION: Permission = Permission::Read;
67 const DESCRIPTION: &'static str =
68 "Verifies a JWT authentication token and returns its permissions.";
69 type Params = (String,);
70 type Ok = Vec<String>;
71 async fn handle(
72 ctx: Ctx,
73 (token,): Self::Params,
74 _: &http::Extensions,
75 ) -> Result<Self::Ok, ServerError> {
76 let ks = ctx.keystore.read();
77 let ki = ks.get(JWT_IDENTIFIER)?;
78 let perms = verify_token(&token, ki.private_key())?;
79 Ok(perms)
80 }
81}
82
83#[serde_as]
84#[derive(Clone, Deserialize, Serialize, JsonSchema)]
85pub struct AuthNewParams {
86 pub perms: Vec<String>,
87 #[serde_as(as = "DurationSeconds<i64>")]
88 #[schemars(with = "i64")]
89 pub token_exp: Duration,
90}
91lotus_json_with_self!(AuthNewParams);
92
93impl AuthNewParams {
94 pub fn process_perms(perm: String) -> Result<Vec<String>, ServerError> {
95 Ok(match perm.to_lowercase().as_str() {
96 "admin" => ADMIN,
97 "sign" => SIGN,
98 "write" => WRITE,
99 "read" => READ,
100 _ => return Err(ServerError::invalid_params("unknown permission", None)),
101 }
102 .iter()
103 .map(ToString::to_string)
104 .collect())
105 }
106}
107
108impl From<AuthNewParams> for (Vec<String>, Option<i64>) {
109 fn from(value: AuthNewParams) -> Self {
110 (value.perms, Some(value.token_exp.num_seconds()))
111 }
112}