use serde_json;
use providers::prelude::*;
use common::prelude::*;
lazy_static! {
static ref GITLAB_EVENTS: Vec<&'static str> = vec![
"Push", "Tag Push", "Issue", "Note", "Merge Request", "Wiki Page",
"Build", "Pipeline", "Confidential Issue",
];
static ref GITLAB_HEADERS: Vec<&'static str> = vec![
"X-Gitlab-Event",
];
}
#[derive(Debug, Deserialize)]
pub struct GitLabProvider {
secret: Option<String>,
events: Option<Vec<String>>,
}
impl ProviderTrait for GitLabProvider {
fn new(config: &str) -> Result<Self> {
let inst: GitLabProvider = serde_json::from_str(config)?;
if let Some(ref events) = inst.events {
for event in events {
if !GITLAB_EVENTS.contains(&event.as_ref()) {
return Err(ErrorKind::ProviderGitLabInvalidEventName(
event.clone()
).into());
}
}
}
Ok(inst)
}
fn validate(&self, request: &Request) -> RequestType {
let req;
if let Request::Web(ref inner) = *request {
req = inner;
} else {
return RequestType::Invalid;
}
for header in GITLAB_HEADERS.iter() {
if !req.headers.contains_key(*header) {
return RequestType::Invalid;
}
}
if let Some(ref secret) = self.secret {
if let Some(token) = req.headers.get("X-Gitlab-Token") {
if token != secret {
return RequestType::Invalid;
}
} else {
return RequestType::Invalid;
}
}
let event = normalize_event_name(&*req.headers["X-Gitlab-Event"]);
if let Some(ref events) = self.events {
if !events.contains(&event.to_string()) {
return RequestType::Invalid;
}
}
if !serde_json::from_str::<serde_json::Value>(&req.body).is_ok() {
return RequestType::Invalid;
}
RequestType::ExecuteHook
}
fn build_env(&self, r: &Request, b: &mut EnvBuilder) -> Result<()> {
let req;
if let Request::Web(ref inner) = *r {
req = inner;
} else {
return Ok(());
}
let event_header =
normalize_event_name(&*req.headers["X-Gitlab-Event"]);
b.add_env("EVENT", event_header);
Ok(())
}
}
fn normalize_event_name(input: &str) -> &str {
if input.ends_with(" Hook") {
let split: Vec<&str> = input.rsplitn(2, ' ').collect();
split[1]
} else {
input
}
}
#[cfg(test)]
mod tests {
use utils::testing::*;
use requests::{Request, RequestType};
use web::WebRequest;
use providers::ProviderTrait;
use scripts::EnvBuilder;
use super::{normalize_event_name, GitLabProvider, GITLAB_EVENTS};
fn base_request() -> WebRequest {
let mut base = dummy_web_request();
base.headers
.insert("X-Gitlab-Event".to_string(), "Push Hook".to_string());
base.body = r#"{"a": "b"}"#.to_string();
base
}
#[test]
fn test_new() {
for right in &[
r#"{}"#,
r#"{"secret": "abcde"}"#,
r#"{"events": ["Push", "Issue"]}"#,
r#"{"secret": "abcde", "events": ["Push", "Issue"]}"#,
] {
assert!(GitLabProvider::new(right).is_ok(), right.to_string());
}
for wrong in &[
r#"{"secret": 12345}"#,
r#"{"secret": true}"#,
r#"{"events": 12345}"#,
r#"{"events": true}"#,
r#"{"events": {}}"#,
r#"{"events": [12345]}"#,
r#"{"events": [true]}"#,
r#"{"events": ["invalid_event"]}"#,
] {
assert!(GitLabProvider::new(wrong).is_err(), wrong.to_string());
}
}
#[test]
fn test_validate_request_type() {
let provider = GitLabProvider::new("{}").unwrap();
for event in GITLAB_EVENTS.iter() {
let mut request = base_request();
request
.headers
.insert("X-Gitlab-Event".to_string(), format!("{} Hook", event));
assert_eq!(
provider.validate(&request.into()),
RequestType::ExecuteHook
);
}
}
#[test]
fn test_validate_basic() {
let provider = GitLabProvider::new("{}").unwrap();
assert_eq!(
provider.validate(&dummy_web_request().into()),
RequestType::Invalid
);
let mut req = dummy_web_request();
req.headers
.insert("X-Gitlab-Event".to_string(), "Push Hook".to_string());
assert_eq!(provider.validate(&req.into()), RequestType::Invalid);
let mut req = dummy_web_request();
req.body = r#"{"a": "b"}"#.to_string();
assert_eq!(provider.validate(&req.into()), RequestType::Invalid);
let mut req = dummy_web_request();
req.headers
.insert("X-Gitlab-Event".to_string(), "Push Hook".to_string());
req.body = r#"{"a": "b"}"#.to_string();
assert_eq!(provider.validate(&req.into()), RequestType::ExecuteHook);
}
#[test]
fn test_validate_secret() {
let provider = GitLabProvider::new(r#"{"secret": "abcde"}"#).unwrap();
let no_secret = GitLabProvider::new("{}").unwrap();
assert_eq!(
no_secret.validate(&base_request().into()),
RequestType::ExecuteHook
);
assert_eq!(
provider.validate(&base_request().into()),
RequestType::Invalid
);
let mut req = base_request();
req.headers
.insert("X-Gitlab-Token".to_string(), "12345".to_string());
assert_eq!(provider.validate(&req.into()), RequestType::Invalid);
let mut req = base_request();
req.headers
.insert("X-Gitlab-Token".to_string(), "abcde".to_string());
assert_eq!(provider.validate(&req.into()), RequestType::ExecuteHook);
}
#[test]
fn test_validate_events() {
let config = r#"{"events": ["Push", "Issue"]}"#;
let provider = GitLabProvider::new(config).unwrap();
fn with_event(name: &str) -> Request {
let mut base = base_request();
base.body = "{}".to_string();
base.headers
.insert("X-Gitlab-Event".to_string(), name.to_string());
Request::Web(base)
}
assert_eq!(
provider.validate(&with_event("Push Hook")),
RequestType::ExecuteHook
);
assert_eq!(
provider.validate(&with_event("Build Hook")),
RequestType::Invalid
);
let provider = GitLabProvider::new("{}").unwrap();
assert_eq!(
provider.validate(&with_event("Push Hook")),
RequestType::ExecuteHook
);
assert_eq!(
provider.validate(&with_event("Build Hook")),
RequestType::ExecuteHook
);
assert_eq!(
provider.validate(&with_event("Strange Hook")),
RequestType::ExecuteHook
);
}
#[test]
fn test_build_env() {
let mut req = base_request();
req.headers.insert("X-Gitlab-Event".into(), "Push Hook".to_string());
let provider = GitLabProvider::new("{}").unwrap();
let mut b = EnvBuilder::dummy();
provider.build_env(&req.into(), &mut b).unwrap();
assert_eq!(b.dummy_data().env, hashmap! {
"EVENT".into() => "Push".into(),
});
assert_eq!(b.dummy_data().files, hashmap!());
}
#[test]
fn test_normalize_event_name() {
assert_eq!(normalize_event_name("Push"), "Push");
assert_eq!(normalize_event_name("Push Hook"), "Push");
assert_eq!(normalize_event_name("Push Hook Hook"), "Push Hook");
}
}