forked from github-starred/komodo
implement auth extension
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@ target
|
||||
node_modules
|
||||
dist
|
||||
.env
|
||||
|
||||
config.json
|
||||
17
core/config.json.example
Normal file
17
core/config.json.example
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"PORT": 9000,
|
||||
"PASSKEY": "your-default-passkey",
|
||||
"DOCKER_ACCOUNTS": {},
|
||||
"GITHUB_ACCOUNTS": {},
|
||||
"GITHUB_OAUTH": {
|
||||
"ID": "your_client_id",
|
||||
"SECRET": "your_client_secret"
|
||||
},
|
||||
"GITHUB_WEBHOOK_SECRET": "your_random_webhook_secret",
|
||||
"JWT_SECRET": "your_jwt_secret",
|
||||
"JWT_VALID_FOR": "1-wk",
|
||||
"SLACK_URL": "your_slack_app_webhook_url",
|
||||
"MONGO_URI": "your_mongo_uri",
|
||||
"MONGO_APP_NAME": "monitor_core",
|
||||
"MONGO_DB_NAME": "monitor"
|
||||
}
|
||||
@@ -1,5 +1,28 @@
|
||||
use axum::Router;
|
||||
use anyhow::anyhow;
|
||||
use axum::{http::StatusCode, middleware, routing::get, Extension, Json, Router};
|
||||
use db::DbExtension;
|
||||
use types::{User, UserId};
|
||||
|
||||
use crate::{auth::auth_request, helpers::handle_anyhow_error, ResponseResult};
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/user",
|
||||
get(|user_id, db| async { get_user(user_id, db).await.map_err(handle_anyhow_error) }),
|
||||
)
|
||||
.layer(middleware::from_fn(auth_request))
|
||||
}
|
||||
|
||||
async fn get_user(
|
||||
Extension(user_id): Extension<UserId>,
|
||||
Extension(db): DbExtension,
|
||||
) -> anyhow::Result<Json<User>> {
|
||||
let mut user = db
|
||||
.users
|
||||
.find_one_by_id(&user_id)
|
||||
.await?
|
||||
.ok_or(anyhow!("did not find user"))?;
|
||||
user.password = None;
|
||||
Ok(Json(user))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ use types::CoreConfig;
|
||||
pub type GithubOauthExtension = Extension<Arc<BasicClient>>;
|
||||
|
||||
pub fn router(config: &CoreConfig) -> Router {
|
||||
Router::new().layer(github_oauth_extension(config, format!("")))
|
||||
Router::new().layer(github_oauth_extension(
|
||||
config,
|
||||
format!("http://localhost:9000/auth/github/callback"),
|
||||
))
|
||||
}
|
||||
|
||||
fn github_oauth_extension(config: &CoreConfig, redirect_url: String) -> GithubOauthExtension {
|
||||
|
||||
@@ -2,22 +2,24 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_timing_util::{get_timelength_in_ms, unix_timestamp_ms, Timelength};
|
||||
use axum::Extension;
|
||||
use axum::{body::Body, http::Request, Extension};
|
||||
use db::DbClient;
|
||||
use hmac::{Hmac, Mac};
|
||||
use jwt::{SignWithKey, VerifyWithKey};
|
||||
use mungos::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use types::CoreConfig;
|
||||
use types::{CoreConfig, User, UserId};
|
||||
|
||||
pub type JwtExtension = Extension<Arc<JwtClient>>;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct JwtClaims {
|
||||
pub id: String,
|
||||
pub id: UserId,
|
||||
pub iat: u128,
|
||||
pub exp: u128,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct JwtClient {
|
||||
key: Hmac<Sha256>,
|
||||
valid_for_ms: u128,
|
||||
@@ -47,12 +49,34 @@ impl JwtClient {
|
||||
Ok(jwt)
|
||||
}
|
||||
|
||||
pub fn validate(&self, jwt: &str) -> anyhow::Result<JwtClaims> {
|
||||
pub async fn authenticate(&self, req: &Request<Body>) -> anyhow::Result<UserId> {
|
||||
let jwt = req
|
||||
.headers()
|
||||
.get("authorization")
|
||||
.ok_or(anyhow!(
|
||||
"no authorization header provided. must be Bearer <jwt_token>"
|
||||
))?
|
||||
.to_str()?
|
||||
.replace("Bearer ", "")
|
||||
.replace("bearer ", "");
|
||||
let claims: JwtClaims = jwt
|
||||
.verify_with_key(&self.key)
|
||||
.context("failed to verify claims")?;
|
||||
if claims.exp < unix_timestamp_ms() {
|
||||
Ok(claims)
|
||||
if claims.exp > unix_timestamp_ms() {
|
||||
let users_collection = &req
|
||||
.extensions()
|
||||
.get::<Arc<DbClient>>()
|
||||
.ok_or(anyhow!("failed at getting db handle"))?
|
||||
.users;
|
||||
let user = users_collection
|
||||
.find_one_by_id(&claims.id)
|
||||
.await?
|
||||
.ok_or(anyhow!("did not find user with id {}", claims.id))?;
|
||||
if user.enabled {
|
||||
Ok(claims.id)
|
||||
} else {
|
||||
Err(anyhow!("user not enabled"))
|
||||
}
|
||||
} else {
|
||||
Err(anyhow!("token has expired"))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
use axum::Router;
|
||||
use types::CoreConfig;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context;
|
||||
use axum::{
|
||||
body::Body,
|
||||
http::{header, Request, StatusCode},
|
||||
middleware::Next,
|
||||
response::{IntoResponse, Response},
|
||||
Router,
|
||||
};
|
||||
use types::{CoreConfig, UserId};
|
||||
|
||||
mod github;
|
||||
mod jwt;
|
||||
@@ -12,3 +21,19 @@ pub fn router(config: &CoreConfig) -> Router {
|
||||
.nest("/local", local::router())
|
||||
.nest("/github", github::router(config))
|
||||
}
|
||||
|
||||
pub async fn auth_request(
|
||||
mut req: Request<Body>,
|
||||
next: Next<Body>,
|
||||
) -> Result<Response, (StatusCode, String)> {
|
||||
let jwt_client = req.extensions().get::<Arc<JwtClient>>().ok_or((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"failed to get jwt client extension".to_string(),
|
||||
))?;
|
||||
let user_id: UserId = jwt_client
|
||||
.authenticate(&req)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::UNAUTHORIZED, format!("error: {e:#?}")))?;
|
||||
req.extensions_mut().insert(user_id);
|
||||
Ok(next.run(req).await)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
net::{IpAddr, SocketAddr},
|
||||
str::FromStr,
|
||||
@@ -10,13 +11,8 @@ use dotenv::dotenv;
|
||||
use mungos::{Deserialize, Mungos};
|
||||
use types::CoreConfig;
|
||||
|
||||
pub async fn load() -> CoreConfig {
|
||||
let config = load_config();
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
fn load_config() -> CoreConfig {
|
||||
let file = File::open("/secrets/secrets.json");
|
||||
todo!()
|
||||
pub fn load() -> CoreConfig {
|
||||
let config_path = env::var("CONFIG_PATH").unwrap_or("./config.json".to_string());
|
||||
let file = File::open(config_path).expect("failed to open config file");
|
||||
serde_json::from_reader(file).unwrap()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use auth::JwtClient;
|
||||
use axum::Router;
|
||||
use axum::{http::StatusCode, Router};
|
||||
use db::DbClient;
|
||||
use docker::DockerClient;
|
||||
use helpers::get_socket_addr;
|
||||
@@ -11,15 +11,19 @@ mod auth;
|
||||
mod config;
|
||||
mod helpers;
|
||||
|
||||
type ResponseResult<T> = Result<T, (StatusCode, String)>;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let config = config::load().await;
|
||||
let config = config::load();
|
||||
|
||||
let app = Router::new()
|
||||
.nest("/api", api::router())
|
||||
.nest("/auth", auth::router(&config))
|
||||
.layer(DbClient::extension((&config).into()).await)
|
||||
.layer(JwtClient::extension(&config));
|
||||
.layer(JwtClient::extension(&config))
|
||||
.layer(DbClient::extension((&config).into()).await);
|
||||
|
||||
println!("starting monitor_core on localhost:{}", config.port);
|
||||
|
||||
axum::Server::bind(&get_socket_addr(&config))
|
||||
.serve(app.into_make_service())
|
||||
|
||||
@@ -7,6 +7,7 @@ use strum_macros::{Display, EnumString};
|
||||
|
||||
pub type PermissionsMap = HashMap<String, PermissionLevel>;
|
||||
|
||||
pub type UserId = String;
|
||||
pub type ServerId = String;
|
||||
pub type DeploymentId = String;
|
||||
pub type BuildId = String;
|
||||
@@ -223,15 +224,28 @@ pub struct OauthCredentials {
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub struct CoreConfig {
|
||||
// port the core web server runs on
|
||||
pub port: u16,
|
||||
|
||||
// default periphery passkey
|
||||
pub passkey: String,
|
||||
|
||||
// docker integration
|
||||
pub docker_accounts: DockerAccounts,
|
||||
|
||||
// github integration
|
||||
pub github_accounts: GithubAccounts,
|
||||
pub github_oauth: OauthCredentials,
|
||||
pub jwt_secret: String,
|
||||
pub slack_token: Option<String>,
|
||||
pub github_webhook_secret: String,
|
||||
|
||||
// jwt config
|
||||
pub jwt_secret: String,
|
||||
pub jwt_valid_for: Timelength,
|
||||
|
||||
// integration with slack app
|
||||
pub slack_url: Option<String>,
|
||||
|
||||
//mongo config
|
||||
pub mongo_uri: String,
|
||||
#[serde(default = "default_core_mongo_app_name")]
|
||||
pub mongo_app_name: String,
|
||||
|
||||
Reference in New Issue
Block a user