feat: HTTP authentication for events endpoint
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Aravinth Manivannan 2022-12-07 13:02:09 +05:30
parent 6145695980
commit f9d23cb3ef
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
4 changed files with 53 additions and 1 deletions

16
Cargo.lock generated
View file

@ -193,6 +193,21 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "actix-web-httpauth"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dda62cf04bc3a9ad2ea8f314f721951cfdb4cdacec4e984d20e77c7bb170991"
dependencies = [
"actix-utils",
"actix-web",
"base64",
"futures-core",
"futures-util",
"log",
"pin-project-lite",
]
[[package]] [[package]]
name = "adler" name = "adler"
version = "1.0.2" version = "1.0.2"
@ -405,6 +420,7 @@ dependencies = [
"actix-rt", "actix-rt",
"actix-web", "actix-web",
"actix-web-codegen-const-routes", "actix-web-codegen-const-routes",
"actix-web-httpauth",
"base64", "base64",
"clap", "clap",
"config", "config",

View file

@ -24,6 +24,7 @@ derive_more = "0.99.17"
url = { version = "2.2.2", features = ["serde"]} url = { version = "2.2.2", features = ["serde"]}
serde_json = { version ="1", features = ["raw_value"]} serde_json = { version ="1", features = ["raw_value"]}
clap = { vesrion = "3.2.20", features = ["derive"]} clap = { vesrion = "3.2.20", features = ["derive"]}
actix-web-httpauth = "0.8.0"
[dependencies.libconductor] [dependencies.libconductor]
path = "./env/libconductor" path = "./env/libconductor"

View file

@ -14,11 +14,33 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
use actix_web::dev::ServiceRequest;
use actix_web::web; use actix_web::web;
use actix_web::Error;
use actix_web_httpauth::extractors::basic::BasicAuth;
use crate::errors::*;
use crate::AppCtx;
use crate::SETTINGS;
pub mod meta; pub mod meta;
pub mod webhook; pub mod webhook;
pub async fn httpauth(
req: ServiceRequest,
credentials: BasicAuth,
) -> Result<ServiceRequest, (Error, ServiceRequest)> {
let _ctx: &AppCtx = req.app_data().unwrap();
let username = credentials.user_id();
let password = credentials.password().unwrap();
if SETTINGS.authenticate(username, password) {
Ok(req)
} else {
let e = Error::from(ServiceError::Unauthorized);
Err((e, req))
}
}
pub const API_V1_ROUTES: routes::Routes = routes::Routes::new(); pub const API_V1_ROUTES: routes::Routes = routes::Routes::new();
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]

View file

@ -15,6 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
use actix_web::{web, HttpResponse, Responder}; use actix_web::{web, HttpResponse, Responder};
use actix_web_httpauth::middleware::HttpAuthentication;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use libconductor::EventType; use libconductor::EventType;
@ -23,6 +24,8 @@ use crate::errors::*;
use crate::AppCtx; use crate::AppCtx;
use crate::*; use crate::*;
use super::httpauth;
pub mod routes { pub mod routes {
use super::*; use super::*;
#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
@ -42,7 +45,10 @@ pub fn services(cfg: &mut web::ServiceConfig) {
cfg.service(post_event); cfg.service(post_event);
} }
#[actix_web_codegen_const_routes::post(path = "API_V1_ROUTES.webhook.post_event")] #[actix_web_codegen_const_routes::post(
path = "API_V1_ROUTES.webhook.post_event",
wrap = "HttpAuthentication::basic(httpauth)"
)]
async fn post_event(ctx: AppCtx, payload: web::Json<EventType>) -> ServiceResult<impl Responder> { async fn post_event(ctx: AppCtx, payload: web::Json<EventType>) -> ServiceResult<impl Responder> {
ctx.conductor.process(payload.into_inner()).await; ctx.conductor.process(payload.into_inner()).await;
Ok(HttpResponse::Created()) Ok(HttpResponse::Created())
@ -65,12 +71,19 @@ pub mod tests {
) )
.await; .await;
let creds = settings.api_keys.get(0).unwrap().clone();
let auth = format!(
"Basic {}",
base64::encode(format!("{}:{}", creds.username.clone(), creds.password))
);
let new_hostname = EventType::NewHostname("demo.librepages.org".into()); let new_hostname = EventType::NewHostname("demo.librepages.org".into());
// upload json // upload json
let upload_json = test::call_service( let upload_json = test::call_service(
&app, &app,
test::TestRequest::post() test::TestRequest::post()
.append_header((actix_web::http::header::AUTHORIZATION, auth.clone()))
.uri(API_V1_ROUTES.webhook.post_event) .uri(API_V1_ROUTES.webhook.post_event)
.set_json(&new_hostname) .set_json(&new_hostname)
.to_request(), .to_request(),