Skip to content

Commit

Permalink
Upgrade wiremock to 0.6.2 (#19)
Browse files Browse the repository at this point in the history
wiremock replaces the `http-types` with the `http` crate.

I've adapt `struct BasicAuth` from `http-types` into the code.
Alternatively wiremock provides `BasicAuthMatcher` which would be
simpler but produce less concrete errors.
  • Loading branch information
em- authored Feb 7, 2025
2 parents 2373ed6 + d98d932 commit 165241b
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 58 deletions.
5 changes: 3 additions & 2 deletions open-build-service-mock/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ license = "MIT OR Apache-2.0"

[dependencies]
base16ct = { version = "0.1", features = ["std"] }
http-types = "2.12"
http = "1.2.0"
md-5 = "0.10"
quick-xml = { version = "0.23.1", features = [ "serialize" ] }
rand = "0.8.5"
serde = "1.0.125"
strum = "0.23"
strum_macros = "0.23"
xml-builder = "=0.5.3"
wiremock = "0.5.22"
wiremock = "0.6.2"
base64ct = { version = "1.6.0", features = ["std"] }
48 changes: 24 additions & 24 deletions open-build-service-mock/src/api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ use super::*;

fn unknown_repo(project: &str, repo: &str) -> ApiError {
ApiError::new(
StatusCode::NotFound,
StatusCode::NOT_FOUND,
"404".to_owned(),
format!("project '{}' has no repository '{}'", project, repo),
)
}

fn unknown_arch(project: &str, repo: &str, arch: &str) -> ApiError {
ApiError::new(
StatusCode::NotFound,
StatusCode::NOT_FOUND,
"404".to_owned(),
format!(
"repository '{}/{}' has no architecture '{}'",
Expand All @@ -31,7 +31,7 @@ fn unknown_arch(project: &str, repo: &str, arch: &str) -> ApiError {

fn unknown_parameter(param: &str) -> ApiError {
ApiError::new(
StatusCode::BadRequest,
StatusCode::BAD_REQUEST,
"400".to_owned(),
format!("unknown parameter '{}'", param),
)
Expand Down Expand Up @@ -61,7 +61,7 @@ impl Respond for ProjectBuildCommandResponder {

let cmd = try_api!(
find_query_param(request, "cmd").ok_or_else(|| ApiError::new(
StatusCode::BadRequest,
StatusCode::BAD_REQUEST,
"missing_parameter".to_string(),
"Missing parameter 'cmd'".to_string()
))
Expand All @@ -76,7 +76,7 @@ impl Respond for ProjectBuildCommandResponder {
"package" => package_names.push(value.clone().into_owned()),
"arch" | "repository" | "code" | "lastbuild" => {
return ApiError::new(
StatusCode::MisdirectedRequest,
StatusCode::MISDIRECTED_REQUEST,
"unsupported".to_string(),
"Operation not supported by the OBS mock server".to_owned(),
)
Expand Down Expand Up @@ -106,7 +106,7 @@ impl Respond for ProjectBuildCommandResponder {
inner_xml.render(&mut inner, false, true).unwrap();

return ApiError::new(
StatusCode::NotFound,
StatusCode::NOT_FOUND,
"not_found".to_owned(),
String::from_utf8_lossy(&inner).into_owned(),
)
Expand Down Expand Up @@ -134,10 +134,10 @@ impl Respond for ProjectBuildCommandResponder {
}
}

ResponseTemplate::new(StatusCode::Ok).set_body_xml(build_status_xml("ok", None))
ResponseTemplate::new(StatusCode::OK).set_body_xml(build_status_xml("ok", None))
}
_ => ApiError::new(
StatusCode::BadRequest,
StatusCode::BAD_REQUEST,
"illegal_request".to_owned(),
format!("unsupported POST command {} to {}", cmd, request.url),
)
Expand Down Expand Up @@ -213,7 +213,7 @@ impl Respond for ArchListingResponder {
xml.add_child(child_xml).unwrap();
}

ResponseTemplate::new(StatusCode::Ok).set_body_xml(xml)
ResponseTemplate::new(StatusCode::OK).set_body_xml(xml)
}
}

Expand Down Expand Up @@ -330,7 +330,7 @@ impl Respond for BuildJobHistoryResponder {
"code" => code_names.push(value.into_owned()),
"limit" if limit.is_some() => {
return ApiError::new(
StatusCode::Ok,
StatusCode::OK,
"400".to_owned(),
"parameter 'limit' set multiple times".to_owned(),
)
Expand Down Expand Up @@ -410,7 +410,7 @@ impl Respond for BuildJobHistoryResponder {
}
}

ResponseTemplate::new(StatusCode::Ok).set_body_xml(xml)
ResponseTemplate::new(StatusCode::OK).set_body_xml(xml)
}
}

Expand Down Expand Up @@ -465,7 +465,7 @@ impl Respond for BuildBinaryListResponder {
}
}

ResponseTemplate::new(StatusCode::Ok).set_body_xml(xml)
ResponseTemplate::new(StatusCode::OK).set_body_xml(xml)
}
}

Expand Down Expand Up @@ -513,11 +513,11 @@ impl Respond for BuildBinaryFileResponder {
let file = try_api!(package
.and_then(|package| package.binaries.get(file_name))
.ok_or_else(|| ApiError::new(
StatusCode::NotFound,
StatusCode::NOT_FOUND,
"404".to_owned(),
format!("{}: No such file or directory", file_name)
)));
ResponseTemplate::new(StatusCode::Ok)
ResponseTemplate::new(StatusCode::OK)
.set_body_raw(file.contents.clone(), "application/octet-stream")
}
}
Expand Down Expand Up @@ -562,7 +562,7 @@ impl Respond for BuildPackageStatusResponder {
.ok_or_else(|| unknown_arch(project_name, repo_name, arch)));

let package = arch.packages.get(package_name);
ResponseTemplate::new(StatusCode::Ok).set_body_xml(package.map_or_else(
ResponseTemplate::new(StatusCode::OK).set_body_xml(package.map_or_else(
|| {
package_status_xml(
package_name,
Expand All @@ -587,15 +587,15 @@ impl BuildLogResponder {
fn parse_number_param(value: Cow<str>) -> Result<usize, ApiError> {
if value.is_empty() {
return Err(ApiError::new(
StatusCode::BadRequest,
StatusCode::BAD_REQUEST,
"400".to_owned(),
"number is empty".to_owned(),
));
}

value.as_ref().parse().map_err(|_| {
ApiError::new(
StatusCode::BadRequest,
StatusCode::BAD_REQUEST,
"400".to_owned(),
format!("not a number: '{}'", value),
)
Expand All @@ -607,7 +607,7 @@ fn parse_bool_param(value: Cow<str>) -> Result<bool, ApiError> {
"1" => Ok(true),
"0" => Ok(false),
_ => Err(ApiError::new(
StatusCode::BadRequest,
StatusCode::BAD_REQUEST,
"400".to_owned(),
"not a boolean".to_owned(),
)),
Expand Down Expand Up @@ -646,7 +646,7 @@ impl Respond for BuildLogResponder {
ensure!(
value == "entry",
ApiError::new(
StatusCode::BadRequest,
StatusCode::BAD_REQUEST,
"400".to_owned(),
format!("unknown view '{}'", value)
)
Expand Down Expand Up @@ -682,7 +682,7 @@ impl Respond for BuildLogResponder {
.get(arch)
.ok_or_else(|| unknown_arch(project_name, repo_name, arch)));
let package = try_api!(arch.packages.get(package_name).ok_or_else(|| ApiError::new(
StatusCode::BadRequest,
StatusCode::BAD_REQUEST,
"400".to_owned(),
format!("remote error: {} no logfile", package_name)
)));
Expand All @@ -706,13 +706,13 @@ impl Respond for BuildLogResponder {
xml.add_child(entry_xml).unwrap();
}

ResponseTemplate::new(StatusCode::Ok).set_body_xml(xml)
ResponseTemplate::new(StatusCode::OK).set_body_xml(xml)
} else {
let contents = log.as_ref().map_or("", |log| &log.contents);
ensure!(
start <= contents.len(),
ApiError::new(
StatusCode::BadRequest,
StatusCode::BAD_REQUEST,
"400".to_owned(),
format!("remote error: start out of range {}", start)
)
Expand All @@ -727,7 +727,7 @@ impl Respond for BuildLogResponder {
.unwrap_or(end),
);

ResponseTemplate::new(StatusCode::Ok).set_body_string(&contents[start..end])
ResponseTemplate::new(StatusCode::OK).set_body_string(&contents[start..end])
}
}
}
Expand Down Expand Up @@ -791,6 +791,6 @@ impl Respond for BuildHistoryResponder {
}
}

ResponseTemplate::new(StatusCode::Ok).set_body_xml(xml)
ResponseTemplate::new(StatusCode::OK).set_body_xml(xml)
}
}
58 changes: 51 additions & 7 deletions open-build-service-mock/src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{borrow::Cow, fmt::Display, time::SystemTime};

use http_types::{auth::BasicAuth, StatusCode};
use http::{header::AUTHORIZATION, StatusCode};
use wiremock::{Request, ResponseTemplate};
use xml_builder::XMLElement;

Expand All @@ -10,6 +10,49 @@ pub(crate) use build::*;
mod source;
pub(crate) use source::*;

// BasicAuth Adapted from http-rs/http-types crate
pub struct BasicAuth {
username: String,
password: String,
}

impl BasicAuth {
pub fn new<U: AsRef<str>, P: AsRef<str>>(username: U, password: P) -> Self {
Self {
username: username.as_ref().to_owned(),
password: password.as_ref().to_owned(),
}
}

pub fn from_credentials(credentials: impl AsRef<[u8]>) -> Result<Self, ()> {
use base64ct::{Base64, Encoding};
let credentials = std::str::from_utf8(credentials.as_ref()).map_err(|_| ())?;
let bytes = Base64::decode_vec(credentials).map_err(|_| ())?;

let credentials = String::from_utf8(bytes).map_err(|_| ())?;

let mut iter = credentials.splitn(2, ':');
let username = iter.next();
let password = iter.next();

let (username, password) = match (username, password) {
(Some(username), Some(password)) => (username.to_string(), password.to_string()),
(Some(_), None) => return Err(()),
(None, _) => return Err(()),
};

Ok(Self { username, password })
}

pub fn username(&self) -> &str {
self.username.as_str()
}

pub fn password(&self) -> &str {
self.password.as_str()
}
}

fn build_status_xml(code: &str, summary: Option<String>) -> XMLElement {
let mut status_xml = XMLElement::new("status");
status_xml.add_attribute("code", code);
Expand Down Expand Up @@ -69,25 +112,26 @@ impl Display for ApiError {

fn unknown_project(project: String) -> ApiError {
ApiError {
http_status: StatusCode::NotFound,
http_status: StatusCode::NOT_FOUND,
code: "unknown_project".to_owned(),
summary: project,
}
}

fn unknown_package(package: String) -> ApiError {
ApiError::new(StatusCode::NotFound, "unknown_package".to_owned(), package)
ApiError::new(StatusCode::NOT_FOUND, "unknown_package".to_owned(), package)
}

fn check_auth(auth: &BasicAuth, request: &Request) -> Result<(), ApiError> {
let given_auth = request
.headers
.get(&"authorization".into())
.and_then(|auth| auth.last().as_str().strip_prefix("Basic "))
.get(AUTHORIZATION)
.and_then(|auth| auth.to_str().ok())
.and_then(|s| s.strip_prefix("Basic "))
.and_then(|creds| BasicAuth::from_credentials(creds.trim().as_bytes()).ok())
.ok_or_else(|| {
ApiError::new(
StatusCode::Unauthorized,
StatusCode::UNAUTHORIZED,
"authentication_required".to_owned(),
"Authentication required".to_owned(),
)
Expand All @@ -97,7 +141,7 @@ fn check_auth(auth: &BasicAuth, request: &Request) -> Result<(), ApiError> {
Ok(())
} else {
Err(ApiError::new(
StatusCode::Unauthorized,
StatusCode::UNAUTHORIZED,
"authentication_required".to_owned(),
format!(
"Unknown user '{}' or invalid password",
Expand Down
Loading

0 comments on commit 165241b

Please sign in to comment.