Support BasicAuth [wip]
To use BasicAuth either a global auth user or a per hook basis can be configured. ToDo: - Add logic to check if a hook BasicAuth is configured, if not check for globaly
This commit is contained in:
parent
2c3319ad84
commit
ff62933ef5
6 changed files with 81 additions and 1 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1873,6 +1873,7 @@ name = "webhookey"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"base64",
|
||||||
"clap",
|
"clap",
|
||||||
"dirs",
|
"dirs",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|
|
@ -28,6 +28,7 @@ ipnet = { version = "2.3", features = ["serde"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
run_script = "0.9"
|
run_script = "0.9"
|
||||||
clap = "3.0.0-beta.5"
|
clap = "3.0.0-beta.5"
|
||||||
|
base64 = "0.13"
|
||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
extended-description = "Webhookey receives requests in form of a so called Webhook as for example sent by Gitea. Those requests are matched against configured filters, if a filter matches, values from the header and the body can be passed to scripts as parameters which are then executed subsequently."
|
extended-description = "Webhookey receives requests in form of a so called Webhook as for example sent by Gitea. Those requests are matched against configured filters, if a filter matches, values from the header and the body can be passed to scripts as parameters which are then executed subsequently."
|
||||||
|
|
74
src/auth.rs
Normal file
74
src/auth.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
use crate::WebhookeyError;
|
||||||
|
use base64;
|
||||||
|
use rocket::{
|
||||||
|
http::Status,
|
||||||
|
outcome::Outcome,
|
||||||
|
request::{self, FromRequest},
|
||||||
|
Request,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
|
fn decode_to_creds<T: Into<String>>(base64_encoded: T) -> Option<(String, String)> {
|
||||||
|
let decoded_creds = match base64::decode(base64_encoded.into()) {
|
||||||
|
Ok(cred_bytes) => String::from_utf8(cred_bytes).unwrap(),
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((username, password)) = decoded_creds.split_once(":") {
|
||||||
|
Some((username.to_string(), password.to_string()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct BasicAuth {
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasicAuth {
|
||||||
|
pub fn new<T: Into<String>>(auth_header: T) -> Option<Self> {
|
||||||
|
let key = auth_header.into();
|
||||||
|
|
||||||
|
if key.len() < 7 || &key[..6] != "Basic " {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (username, password) = decode_to_creds(&key[6..])?;
|
||||||
|
Some(Self { username, password })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for BasicAuth {
|
||||||
|
type Error = WebhookeyError;
|
||||||
|
|
||||||
|
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
let keys: Vec<_> = request.headers().get("Authorization").collect();
|
||||||
|
|
||||||
|
match keys.len() {
|
||||||
|
0 => Outcome::Forward(()),
|
||||||
|
1 => match BasicAuth::new(keys[0]) {
|
||||||
|
Some(auth_header) => Outcome::Success(auth_header),
|
||||||
|
None => Outcome::Failure((
|
||||||
|
Status::Unauthorized,
|
||||||
|
WebhookeyError::Unauthorized(
|
||||||
|
request
|
||||||
|
.client_ip()
|
||||||
|
.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED)),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
_ => Outcome::Failure((
|
||||||
|
Status::Unauthorized,
|
||||||
|
WebhookeyError::Unauthorized(
|
||||||
|
request
|
||||||
|
.client_ip()
|
||||||
|
.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED)),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{filters::IpFilter, hooks::Hook};
|
use crate::{auth::BasicAuth, filters::IpFilter, hooks::Hook};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -15,6 +15,7 @@ pub struct MetricsConfig {
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub metrics: Option<MetricsConfig>,
|
pub metrics: Option<MetricsConfig>,
|
||||||
|
pub auth: Option<BasicAuth>,
|
||||||
pub hooks: BTreeMap<String, Hook>,
|
pub hooks: BTreeMap<String, Hook>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
auth::BasicAuth,
|
||||||
filters::{FilterType, IpFilter},
|
filters::{FilterType, IpFilter},
|
||||||
Config, Metrics, WebhookeyError,
|
Config, Metrics, WebhookeyError,
|
||||||
};
|
};
|
||||||
|
@ -52,6 +53,7 @@ fn validate_request(secret: &str, signature: &str, data: &[u8]) -> Result<()> {
|
||||||
pub struct Hook {
|
pub struct Hook {
|
||||||
command: String,
|
command: String,
|
||||||
signature: String,
|
signature: String,
|
||||||
|
auth: Option<BasicAuth>,
|
||||||
ip_filter: Option<IpFilter>,
|
ip_filter: Option<IpFilter>,
|
||||||
secrets: Vec<String>,
|
secrets: Vec<String>,
|
||||||
filter: FilterType,
|
filter: FilterType,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod auth;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod config;
|
mod config;
|
||||||
mod filters;
|
mod filters;
|
||||||
|
|
Loading…
Reference in a new issue