Skip to content

Commit

Permalink
auth-server: Add management key and gate key management API
Browse files Browse the repository at this point in the history
  • Loading branch information
joeykraut committed Oct 24, 2024
1 parent 0049346 commit 27374ba
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 23 deletions.
4 changes: 2 additions & 2 deletions auth/auth-server-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#![deny(clippy::needless_pass_by_ref_mut)]
#![feature(trivial_bounds)]

use serde::Deserialize;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// The Renegade API key header
Expand All @@ -26,7 +26,7 @@ pub const API_KEYS_PATH: &str = "api-keys";
pub const DEACTIVATE_API_KEY_PATH: &str = "/api-keys/{id}/deactivate";

/// A request to create a new API key
#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateApiKeyRequest {
/// The API key id
pub id: Uuid,
Expand Down
28 changes: 24 additions & 4 deletions auth/auth-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#![deny(clippy::missing_docs_in_private_items)]
#![deny(unsafe_code)]
#![deny(clippy::needless_pass_by_ref_mut)]
#![deny(clippy::needless_pass_by_value)]
#![feature(trivial_bounds)]

pub(crate) mod error;
Expand Down Expand Up @@ -48,6 +49,10 @@ pub struct Cli {
/// The encryption key used to encrypt/decrypt database values
#[arg(long, env = "ENCRYPTION_KEY")]
pub encryption_key: String,
/// The management key for the auth server, used to authenticate management
/// requests
#[arg(long, env = "MANAGEMENT_KEY")]
pub management_key: String,
/// The URL of the relayer
#[arg(long, env = "RELAYER_URL")]
pub relayer_url: String,
Expand Down Expand Up @@ -86,6 +91,12 @@ impl ApiError {
pub fn internal<T: ToString>(msg: T) -> Self {
Self::InternalError(msg.to_string())
}

/// Create a new bad request error
#[allow(clippy::needless_pass_by_value)]
pub fn bad_request<T: ToString>(msg: T) -> Self {
Self::BadRequest(msg.to_string())
}
}

// Implement warp::reject::Reject for ApiError
Expand Down Expand Up @@ -126,17 +137,26 @@ async fn main() {
// Add an API key
let add_api_key = warp::path(API_KEYS_PATH)
.and(warp::post())
.and(warp::body::json())
.and(warp::path::full())
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.and(with_server(server.clone()))
.and_then(|request, server: Arc<Server>| async move { server.add_key(request).await });
.and_then(|path, headers, body, server: Arc<Server>| async move {
server.add_key(path, headers, body).await
});

// Expire an API key
let expire_api_key = warp::path(API_KEYS_PATH)
.and(warp::path::param::<Uuid>())
.and(warp::path("deactivate"))
.and(warp::path::full())
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.and(warp::post())
.and(with_server(server.clone()))
.and_then(|id: Uuid, server: Arc<Server>| async move { server.expire_key(id).await });
.and_then(|id, path, headers, body, server: Arc<Server>| async move {
server.expire_key(id, path, headers, body).await
});

// --- Proxied Routes --- //

Expand All @@ -155,7 +175,7 @@ async fn main() {
// Bind the server and listen
info!("Starting auth server on port {}", listen_addr.port());
let routes =
ping.or(add_api_key).or(expire_api_key).or(atomic_match_path).recover(handle_rejection);
ping.or(atomic_match_path).or(expire_api_key).or(add_api_key).recover(handle_rejection);
warp::serve(routes).bind(listen_addr).await;
}

Expand Down
12 changes: 12 additions & 0 deletions auth/auth-server/src/server/api_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,24 @@ use http::HeaderMap;
use renegade_api::auth::validate_expiring_auth;
use renegade_common::types::wallet::keychain::HmacKey;
use uuid::Uuid;
use warp::filters::path::FullPath;

use crate::{error::AuthServerError, ApiError};

use super::{helpers::aes_decrypt, Server};

impl Server {
/// Authorize a management request
pub fn authorize_management_request(
&self,
path: &FullPath,
headers: &HeaderMap,
body: &[u8],
) -> Result<(), ApiError> {
validate_expiring_auth(path.as_str(), headers, body, &self.management_key)
.map_err(|_| ApiError::Unauthorized)
}

/// Authorize a request
pub(crate) async fn authorize_request(
&self,
Expand Down
38 changes: 29 additions & 9 deletions auth/auth-server/src/server/handle_key_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

use crate::models::NewApiKey;
use auth_server_api::CreateApiKeyRequest;
use bytes::Bytes;
use http::HeaderMap;
use uuid::Uuid;
use warp::{reject::Rejection, reply::Reply};
use warp::{filters::path::FullPath, reject::Rejection, reply::Reply};

use crate::ApiError;

Expand All @@ -14,22 +16,40 @@ use super::{

impl Server {
/// Add a new API key to the database
pub async fn add_key(&self, req: CreateApiKeyRequest) -> Result<impl Reply, Rejection> {
pub async fn add_key(
&self,
path: FullPath,
headers: HeaderMap,
body: Bytes,
) -> Result<impl Reply, Rejection> {
// Check management auth on the request
self.authorize_management_request(&path, &headers, &body)?;

// Deserialize the request
let req: CreateApiKeyRequest =
serde_json::from_slice(&body).map_err(ApiError::bad_request)?;

// Add the key to the database
let encrypted_secret = aes_encrypt(&req.secret, &self.encryption_key)?;
let new_key = NewApiKey::new(req.id, encrypted_secret, req.description);
self.add_key_query(new_key)
.await
.map_err(|e| warp::reject::custom(ApiError::InternalError(e.to_string())))?;
self.add_key_query(new_key).await.map_err(ApiError::internal)?;

Ok(empty_json_reply())
}

/// Expire an existing API key
pub async fn expire_key(&self, key_id: Uuid) -> Result<impl Reply, Rejection> {
self.expire_key_query(key_id)
.await
.map_err(|e| warp::reject::custom(ApiError::InternalError(e.to_string())))?;
pub async fn expire_key(
&self,
key_id: Uuid,
path: FullPath,
headers: HeaderMap,
body: Bytes,
) -> Result<impl Reply, Rejection> {
// Check management auth on the request
self.authorize_management_request(&path, &headers, &body)?;

// Expire the key
self.expire_key_query(key_id).await?;
Ok(empty_json_reply())
}
}
14 changes: 9 additions & 5 deletions auth/auth-server/src/server/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ pub fn aes_decrypt(value: &str, key: &[u8]) -> Result<String, AuthServerError> {

#[cfg(test)]
mod tests {
use renegade_common::types::wallet::keychain::HmacKey;

use super::*;

/// Tests AES encryption and decryption
Expand All @@ -66,11 +68,13 @@ mod tests {
assert_eq!(value, decrypted);
}

/// Generate an encryption key, base64 encode it, and print it
/// Generate a management key
///
/// Useful for local testing
#[test]
pub fn generate_encryption_key() {
let key = Aes128Gcm::generate_key(&mut thread_rng());
let encoded = general_purpose::STANDARD.encode(&key);
println!("{}", encoded);
fn test_generate_management_key() {
let key = HmacKey::random();
let encoded = general_purpose::STANDARD.encode(key.0);
println!("management key: {encoded}");
}
}
11 changes: 8 additions & 3 deletions auth/auth-server/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub struct Server {
pub relayer_url: String,
/// The admin key for the relayer
pub relayer_admin_key: HmacKey,
/// The management key for the auth server
pub management_key: HmacKey,
/// The encryption key for storing API secrets
pub encryption_key: Vec<u8>,
/// The HTTP client
Expand All @@ -53,18 +55,21 @@ impl Server {
// Setup the DB connection pool
let db_pool = create_db_pool(&args.database_url).await?;

// Parse the decryption key as a base64 encoded string
// Parse the decryption key, management key, and relayer admin key as
// base64 encoded strings
let encryption_key = general_purpose::STANDARD
.decode(&args.encryption_key)
.map_err(AuthServerError::encryption)?;

let management_key =
HmacKey::from_base64_string(&args.management_key).map_err(AuthServerError::setup)?;
let relayer_admin_key =
HmacKey::from_base64_string(&args.relayer_admin_key).map_err(AuthServerError::setup)?;

Ok(Self {
db_pool: Arc::new(db_pool),
relayer_url: args.relayer_url,
relayer_admin_key,
management_key,
encryption_key,
client: Client::new(),
})
Expand All @@ -84,7 +89,7 @@ impl Server {
body: Bytes,
) -> Result<Response<Bytes>, ApiError> {
// Admin authenticate the request
self.admin_authenticate(path, &mut headers, &body).await?;
self.admin_authenticate(path, &mut headers, &body)?;

// Forward the request to the relayer
let url = format!("{}{}", self.relayer_url, path);
Expand Down

0 comments on commit 27374ba

Please sign in to comment.