// Create actix actors and path the reference of the task_actor to the queue_actor
// The queue_actor will send it's own address in the StartTask payload for bidirectional communication
let task_actor = SyncArbiter::start(settings.workers, move || TaskActor { queue_actor: None });
- let queue_actor = QueueActor {
- task_actor: task_actor.clone(),
- own_addr: None,
- settings: settings.clone(),
- };
+ let queue_actor = QueueActor::new(
+ task_actor.clone(),
+ settings.clone(),
+ );
init_web_server(queue_actor.start(), settings);
pub task_actor: Addr<TaskActor>,
pub own_addr: Option<Addr<Self>>,
pub settings: Settings,
+ pub current_workers: i32,
}
impl Actor for QueueActor {
}
impl QueueActor {
+ pub fn new(
+ task_actor: Addr<TaskActor>,
+ settings: Settings,
+ ) -> Self {
+ QueueActor {
+ task_actor: task_actor.clone(),
+ own_addr: None,
+ settings: settings.clone(),
+ current_workers: 0,
+ }
+ }
+
fn dispatch_task(&mut self, new_task: NewTask) {
let addr = self.own_addr.as_ref().unwrap().clone();
pub domain: String,
pub port: i32,
pub ssh_cert: Option<String>,
+ pub secret: Option<String>,
pub workers: usize,
pub webhooks: Vec<Webhook>,
}
Settings {
domain: self.domain.clone(),
port: self.port,
+ secret: self.secret.clone(),
ssh_cert: self.ssh_cert.clone(),
workers: self.workers,
webhooks: webhooks,
pub fn get_task_from_request(
settings: &Settings,
name: String,
- params: HashMap<String, String>,
+ parameters : Option<HashMap<String, String>>,
) -> Result<NewTask, HttpResponse> {
- let webhook = settings.get_webhook_by_name(name)?;
+ let parameters = parameters.unwrap_or_default();
- let command = verify_template_parameters(webhook.command, ¶ms)?;
+ let webhook = settings.get_webhook_by_name(name)?;
+ let command = verify_template_parameters(webhook.command, ¶meters)?;
Ok(NewTask {
name: webhook.name,
- parameters: params,
+ parameters: parameters,
cwd: webhook.cwd,
command: command,
})
/// Verify that the template renders with the given parameters
pub fn verify_template_parameters(
template: String,
- params: &HashMap<String, String>,
+ parameters: &HashMap<String, String>,
) -> Result<String, HttpResponse> {
- info!("Got parameters: {:?}", params);
+ info!("Got parameters: {:?}", parameters);
// Create a new handlebar instance and enable strict mode to prevent missing or malformed arguments
let mut handlebars = Handlebars::new();
handlebars.set_strict_mode(true);
// Check the template for render errors with the current parameter
- let result = handlebars.render_template(&template, params);
+ let result = handlebars.render_template(&template, parameters);
match result {
Err(error) => {
Err(HttpResponse::build(StatusCode::BAD_REQUEST).json(format!("{:?}", error)))
use ::actix::prelude::*;
use ::actix_web::*;
+use ::serde::Deserialize;
use ::log::info;
use ::std::collections::HashMap;
settings: Settings,
}
-/// Index route
+#[derive(Deserialize)]
+struct Payload {
+ secret: Option<String>,
+ parameters: Option<HashMap<String, String>>,
+}
+
+
+/// Get index route
fn webhook(
data: web::Data<AppState>,
- query: web::Query<HashMap<String, String>>,
path_info: web::Path<String>,
+ json: web::Json<Payload>,
) -> Result<HttpResponse, HttpResponse> {
// Verify that the parameters match the required parameters in the template string
- let params = query.into_inner();
+ let payload = json.into_inner();
let webhook_name = path_info.into_inner();
info!("");
info!("Incoming webhook for \"{}\":", webhook_name);
+ verify_secret(&payload, &data)?;
+
// Create a new task with the checked parameters and webhook name
- let new_task = get_task_from_request(&data.settings, webhook_name, params)?;
+ let new_task = get_task_from_request(&data.settings, webhook_name, payload.parameters)?;
// Send the task to the actor managing the queue
data.queue_actor.do_send(new_task);
Ok(HttpResponse::Ok().finish())
}
+
+// If a secret is specified in the config file, verify, that the secret exists in the payload
+fn verify_secret(payload: &Payload, data: &web::Data<AppState>) -> Result<(), HttpResponse> {
+ let secret: String;
+ // Accept the payload, if no secret is verified or the secret is an empty string.
+ match data.settings.secret.as_ref() {
+ Some(value) => {
+ secret = value.clone();
+ if secret == "" {
+ return Ok(())
+ }
+ },
+ None => {
+ return Ok(());
+ },
+ }
+
+ // At this point we know that a secret is required. Check if it exists in the payload
+ match payload.secret.as_ref() {
+ Some(payload_secret) => {
+ if *payload_secret == secret {
+ return Ok(())
+ }
+ Err(HttpResponse::Unauthorized()
+ .body("Wrong secret"))
+ },
+ None => Err(HttpResponse::Unauthorized()
+ .body("No secret specified"))
+ }
+}
+
+
+
/// Initialize the web server
/// Move the address of the queue actor inside the AppState for further dispatch
/// of tasks to the actor
queue_actor: queue_actor.clone(),
settings: settings_for_app.clone(),
})
- .service(web::resource("/{webhook_name}").to(webhook))
+ .service(web::resource("/{webhook_name}").route(web::post().to(webhook)))
})
.bind(format!("{}:{}", settings.domain, settings.port))
.unwrap()
port: 8000
ssh_cert: null
workers: 10
+secret: 'thisisasecret'
webhooks:
-
name: 'ls'