Skip to content

Commit af9347f

Browse files
authored
Param loading refactor (#404)
* Key configuration refactoring * Lint * Add test with base path
1 parent 304a8bf commit af9347f

File tree

14 files changed

+658
-384
lines changed

14 files changed

+658
-384
lines changed

v-api-param/src/lib.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,16 @@ impl StringParam {
5454
///
5555
/// For inline values, returns the value directly.
5656
/// For path-based values, reads the file contents and trims trailing whitespace.
57-
pub fn resolve(&self) -> Result<SecretString, ParamResolutionError> {
57+
pub fn resolve(&self, base: Option<PathBuf>) -> Result<SecretString, ParamResolutionError> {
5858
match self {
5959
StringParam::Inline(value) => Ok(value.clone()),
6060
StringParam::FromPath { path } => {
61-
let content = std::fs::read_to_string(path).map_err(|source| {
61+
let path = if let Some(base) = base {
62+
base.join(path)
63+
} else {
64+
path.clone()
65+
};
66+
let content = std::fs::read_to_string(&path).map_err(|source| {
6267
ParamResolutionError::FileRead {
6368
path: path.display().to_string(),
6469
source,
@@ -93,7 +98,7 @@ mod tests {
9398
#[test]
9499
fn test_inline_value() {
95100
let param = StringParam::Inline("my-param".to_string().into());
96-
assert_eq!(param.resolve().unwrap().expose_secret(), "my-param");
101+
assert_eq!(param.resolve(None).unwrap().expose_secret(), "my-param");
97102
}
98103

99104
#[test]
@@ -104,7 +109,23 @@ mod tests {
104109
let param = StringParam::FromPath {
105110
path: file.path().to_path_buf(),
106111
};
107-
assert_eq!(param.resolve().unwrap().expose_secret(), "file-param");
112+
assert_eq!(param.resolve(None).unwrap().expose_secret(), "file-param");
113+
}
114+
115+
#[test]
116+
fn test_from_path_with_base() {
117+
let mut file = NamedTempFile::new().unwrap();
118+
write!(file, "file-param").unwrap();
119+
120+
let param = StringParam::FromPath {
121+
path: PathBuf::from(file.path().file_name().unwrap()),
122+
};
123+
let base_path = std::env::temp_dir();
124+
125+
assert_eq!(
126+
param.resolve(Some(base_path)).unwrap().expose_secret(),
127+
"file-param"
128+
);
108129
}
109130

110131
#[test]
@@ -116,15 +137,15 @@ mod tests {
116137
let param = StringParam::FromPath {
117138
path: file.path().to_path_buf(),
118139
};
119-
assert_eq!(param.resolve().unwrap().expose_secret(), "file-param");
140+
assert_eq!(param.resolve(None).unwrap().expose_secret(), "file-param");
120141
}
121142

122143
#[test]
123144
fn test_from_path_file_not_found() {
124145
let param = StringParam::FromPath {
125146
path: PathBuf::from("/nonexistent/path"),
126147
};
127-
let result = param.resolve();
148+
let result = param.resolve(None);
128149
assert!(matches!(result, Err(ParamResolutionError::FileRead { .. })));
129150
}
130151

@@ -139,7 +160,7 @@ mod tests {
139160

140161
let config: Config = toml::from_str(toml).unwrap();
141162
assert_eq!(
142-
config.key.resolve().unwrap().expose_secret(),
163+
config.key.resolve(None).unwrap().expose_secret(),
143164
"inline-value"
144165
);
145166
}
@@ -157,6 +178,9 @@ mod tests {
157178
}
158179

159180
let config: Config = toml::from_str(&toml).unwrap();
160-
assert_eq!(config.key.resolve().unwrap().expose_secret(), "path-value");
181+
assert_eq!(
182+
config.key.resolve(None).unwrap().expose_secret(),
183+
"path-value"
184+
);
161185
}
162186
}

v-api/src/authn/jwt.rs

Lines changed: 9 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,25 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
use std::{fmt::Debug, sync::Arc};
6-
75
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
86
use chrono::{DateTime, Utc};
97
use jsonwebtoken::{
108
decode, decode_header,
11-
jwk::{
12-
AlgorithmParameters, CommonParameters, Jwk, KeyAlgorithm, PublicKeyUse, RSAKeyParameters,
13-
RSAKeyType,
14-
},
9+
jwk::{AlgorithmParameters, Jwk},
1510
Algorithm, DecodingKey, Header, Validation,
1611
};
1712
use newtype_uuid::TypedUuid;
18-
use rsa::traits::PublicKeyParts;
1913
use serde::{Deserialize, Serialize};
20-
use tap::TapFallible;
14+
use std::{fmt::Debug, sync::Arc};
2115
use thiserror::Error;
2216
use tracing::instrument;
2317
use v_model::{AccessTokenId, UserId, UserProviderId};
2418

25-
use crate::{config::AsymmetricKey, context::VContext, permissions::VAppPermission};
19+
use crate::{authn::Signer, context::VContext, permissions::VAppPermission};
2620

27-
use super::{Signer, SigningKeyError};
21+
use super::SigningKeyError;
22+
23+
pub static DEFAULT_JWT_EXPIRATION: i64 = 3600;
2824

2925
#[derive(Debug, Error)]
3026
pub enum JwtError {
@@ -153,20 +149,15 @@ pub struct JwtSigner {
153149
#[allow(dead_code)]
154150
header: Header,
155151
encoded_header: String,
156-
signer: Arc<dyn Signer>,
152+
signer: Arc<Signer>,
157153
}
158154

159155
impl JwtSigner {
160-
pub fn new(key: &AsymmetricKey) -> Result<Self, JwtSignerError> {
156+
pub fn new(signer: Arc<Signer>) -> Result<Self, JwtSignerError> {
161157
let mut header = Header::new(Algorithm::RS256);
162-
header.kid = Some(key.kid().to_string());
158+
header.kid = Some(signer.kid.clone());
163159
let encoded_header = to_base64_json(&header)?;
164160

165-
let signer = key
166-
.as_signer()
167-
.map_err(JwtSignerError::InvalidKey)
168-
.tap_err(|err| tracing::error!(?err, "Unable to construct signer for JWT key"))?;
169-
170161
Ok(Self {
171162
header,
172163
encoded_header,
@@ -200,31 +191,6 @@ impl JwtSigner {
200191
}
201192
}
202193

203-
impl AsymmetricKey {
204-
pub fn as_jwk(&self) -> Result<Jwk, JwtSignerError> {
205-
let key_id = self.kid();
206-
let public_key = self.public_key().map_err(JwtSignerError::InvalidKey)?;
207-
208-
Ok(Jwk {
209-
common: CommonParameters {
210-
public_key_use: Some(PublicKeyUse::Signature),
211-
key_operations: None,
212-
key_algorithm: Some(KeyAlgorithm::RS256),
213-
key_id: Some(key_id.to_string()),
214-
x509_chain: None,
215-
x509_sha1_fingerprint: None,
216-
x509_sha256_fingerprint: None,
217-
x509_url: None,
218-
},
219-
algorithm: AlgorithmParameters::RSA(RSAKeyParameters {
220-
key_type: RSAKeyType::RSA,
221-
n: URL_SAFE_NO_PAD.encode(public_key.n().to_bytes_be()),
222-
e: URL_SAFE_NO_PAD.encode(public_key.e().to_bytes_be()),
223-
}),
224-
})
225-
}
226-
}
227-
228194
fn to_base64_json<T>(value: &T) -> Result<String, serde_json::error::Error>
229195
where
230196
T: Serialize,

v-api/src/authn/key.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use secrecy::{ExposeSecret, SecretSlice, SecretString};
88
use thiserror::Error;
99
use uuid::Uuid;
1010

11-
use crate::authn::Verifier;
11+
use crate::authn::{Sign, Verify};
1212

13-
use super::{Signer, SigningKeyError};
13+
use super::SigningKeyError;
1414

1515
pub struct RawKey {
1616
clear: SecretSlice<u8>,
@@ -49,7 +49,7 @@ impl RawKey {
4949
&self.clear.expose_secret()[0..16]
5050
}
5151

52-
pub async fn sign(self, signer: &dyn Signer) -> Result<SignedKey, ApiKeyError> {
52+
pub async fn sign(self, signer: &dyn Sign) -> Result<SignedKey, ApiKeyError> {
5353
let signature = hex::encode(
5454
signer
5555
.sign(self.clear.expose_secret())
@@ -64,7 +64,7 @@ impl RawKey {
6464

6565
pub fn verify<T>(&self, verifier: &T, signature: &[u8]) -> Result<(), ApiKeyError>
6666
where
67-
T: Verifier,
67+
T: Verify,
6868
{
6969
let signature = hex::decode(signature)?;
7070
if verifier
@@ -143,15 +143,15 @@ mod tests {
143143

144144
use super::RawKey;
145145
use crate::{
146-
authn::{VerificationResult, Verifier},
146+
authn::{VerificationResult, Verify},
147147
util::tests::{mock_key, MockKey},
148148
};
149149

150150
struct TestVerifier {
151-
verifier: Arc<dyn Verifier>,
151+
verifier: Arc<dyn Verify>,
152152
}
153153

154-
impl Verifier for TestVerifier {
154+
impl Verify for TestVerifier {
155155
fn verify(&self, message: &[u8], signature: &[u8]) -> VerificationResult {
156156
self.verifier.verify(message, signature)
157157
}
@@ -161,13 +161,13 @@ mod tests {
161161
async fn test_verifies_signature() {
162162
let id = Uuid::new_v4();
163163
let MockKey { signer, verifier } = mock_key("test");
164-
let signer = signer.as_signer().unwrap();
164+
let signer = signer.resolve_signer(None).unwrap();
165165
let verifier = TestVerifier {
166-
verifier: verifier.as_verifier().unwrap(),
166+
verifier: Arc::new(verifier.resolve_verifier(None).await.unwrap()),
167167
};
168168

169169
let raw = RawKey::generate::<8>(&id);
170-
let signed = raw.sign(&*signer).await.unwrap();
170+
let signed = raw.sign(&signer).await.unwrap();
171171

172172
let raw2 = RawKey::try_from(signed.key.expose_secret()).unwrap();
173173

@@ -181,13 +181,13 @@ mod tests {
181181
async fn test_generates_signatures() {
182182
let id = Uuid::new_v4();
183183
let MockKey { signer, .. } = mock_key("test");
184-
let signer = signer.as_signer().unwrap();
184+
let signer = signer.resolve_signer(None).unwrap();
185185

186186
let raw1 = RawKey::generate::<8>(&id);
187-
let signed1 = raw1.sign(&*signer).await.unwrap();
187+
let signed1 = raw1.sign(&signer).await.unwrap();
188188

189189
let raw2 = RawKey::generate::<8>(&id);
190-
let signed2 = raw2.sign(&*signer).await.unwrap();
190+
let signed2 = raw2.sign(&signer).await.unwrap();
191191

192192
assert_ne!(signed1.signature(), signed2.signature())
193193
}

0 commit comments

Comments
 (0)