diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 142 |
1 files changed, 109 insertions, 33 deletions
diff --git a/src/main.rs b/src/main.rs index 150c993..074b580 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,7 +55,7 @@ impl Auth { #[derive(Debug)] enum AuthMethod { Basic, - Bearer{realm: String}, + Bearer{realm: String, service: String, scope: String}, } #[derive(Debug)] @@ -121,12 +121,14 @@ fn parse_quotedstring(str: &Vec<char>, start: usize) -> Option<(Range<usize>, us } fn parse_sp(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { + if str.len() <= start { + return None; + } + let mut pos = start; - loop { - match str[pos] { - ' ' => pos += 1, - _ => break, - } + match str[pos] { + ' ' => pos += 1, + _ => return None, } if pos == start { @@ -137,6 +139,10 @@ fn parse_sp(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { } fn parse_crlf(str: &Vec<char>, start: usize) -> Option<usize> { + if str.len() - 2 < start { + return None; + } + if str[start] == '\r' && str[start+1] == '\n' { return Some(start + 2); } @@ -152,7 +158,10 @@ fn parse_lws(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { None => {}, } - (_, pos) = parse_sp(str, pos).unwrap(); + match parse_sp(str, pos) { + Some((_, npos)) => pos = npos, + None => return None, + } loop { match parse_sp(str, pos) { Some((_, npos)) => pos = npos, @@ -209,19 +218,48 @@ fn parse_challenge(str: &Vec<char>, start: usize) -> Option<(Challenge, usize)> let mut params = vec!(); + let mut pos = pos; + loop { + match parse_lws(str, pos) { + Some((_, npos)) => pos = npos, + None => break, + } + } let (param, pos) = parse_param(str, pos).unwrap(); params.push(param); let mut pos = pos; loop { + loop { + match parse_lws(str, pos) { + Some((_, npos)) => pos = npos, + None => break, + } + } + + if pos >= str.len() || str[pos] != ',' { + break; + } + pos += 1; + + loop { + match parse_lws(str, pos) { + Some((_, npos)) => pos = npos, + None => break, + } + } + match parse_param(str, pos) { Some((param, npos)) => { pos = npos; params.push(param); }, - None => break, + None => return None, } } + if pos < str.len() { + return None; + } return Some((Challenge {scheme: scheme_range, params}, pos)); } @@ -236,13 +274,23 @@ impl AuthMethod { if &header_str[parse.scheme.clone()] == "Bearer" { let mut realm_param = None; + let mut scope_param = None; + let mut service_param = None; for param in parse.params { - if &header_str[param.key.clone()] == "realm" { - realm_param = Some(param); + dbg!(&header_str[param.key.clone()]); + match &header_str[param.key.clone()] { + "realm" => realm_param = Some(param), + "scope" => scope_param = Some(param), + "service" => service_param = Some(param), + _ => {}, } } - return Some(AuthMethod::Bearer{realm: header_str[realm_param.unwrap().value].to_string()}); + return Some(AuthMethod::Bearer{ + realm: header_str[realm_param.unwrap().value].to_string(), + scope: header_str[scope_param.unwrap().value].to_string(), + service: header_str[service_param.unwrap().value].to_string(), + }); } return None; @@ -271,16 +319,41 @@ fn perform_registry_request(registry: &str, url: String, auth: &mut Auth) -> Res if let Some(ref mut state) = state { if let AuthStage::Idle = state.stage { if let Some(auth_header) = response.headers().get("www-authenticate") { - dbg!(AuthMethod::from_header(auth_header)); - let auth_header = auth_header.to_str().unwrap(); - if auth_header.starts_with("Basic") { - let basic_auth = format!("Basic {}", BASE64_STANDARD.encode(format!("{}:{}", state.info.username, state.info.password))); - state.stage = AuthStage::Authorized(basic_auth); - - continue; // try again - } else if auth_header.starts_with("Bearer") { - } else { - todo!("Implement other authentication scheme"); + let auth_header = AuthMethod::from_header(auth_header); + match auth_header { + Some(AuthMethod::Basic) => { + let basic_auth = format!("Basic {}", BASE64_STANDARD.encode(format!("{}:{}", state.info.username, state.info.password))); + state.stage = AuthStage::Authorized(basic_auth); + + continue; // try again + }, + Some(AuthMethod::Bearer{realm, scope, service}) => { + let basic_auth = format!("Basic {}", BASE64_STANDARD.encode(format!("{}:{}", state.info.username, state.info.password))); + + let url = format!("{}?service={}&scope={}", realm, service, scope); + dbg!(&url); + + let body = ureq::get(url) + .config().http_status_as_error(false).build() + .header("Authorization", basic_auth) + .call(); + + dbg!(&body); + + let body = body.unwrap() + .body_mut().read_to_string().unwrap(); + + dbg!(&body); + + let body: tinyjson::JsonValue = body + .parse().unwrap(); + + let token: &String = body["token"].get().unwrap(); + + state.stage = AuthStage::Authorized(format!("Bearer {}", token)); + continue; + }, + None => todo!(), } } else { todo!("Server didn't ask us to authenticate"); @@ -454,27 +527,30 @@ fn fetch_new_image(file: &str, auth: &mut Auth, img: DockerRef, edits: &mut Vec< let registry = img.registry.map(|x| &file[x]).unwrap_or("registry.jnsn.dev/"); let tag = img.tag.map(|x| &file[x]).unwrap_or("latest"); - // Find the digest for the newest image + let digest = None; + + // Find the digest for the tag if let Some(digest) = img.digest { let url = format!("/v2/{}/manifests/{}", &file[img.image], tag); let mut response = perform_registry_request(registry, url, auth).unwrap(); let body: tinyjson::JsonValue = response.body_mut().read_to_string().unwrap().parse().unwrap(); + dbg!(&body); let media_type : &String = body["mediaType"].get().unwrap(); - assert!(media_type == "application/vnd.docker.distribution.manifest.v2+json"); + // assert!(media_type == "application/vnd.docker.distribution.manifest.v2+json"); + } - let image_ref = { - let prefix = &file[img.full_range.start..digest.start]; - let digest = &response.headers()["docker-content-digest"].to_str().unwrap(); - format!("{}{}", prefix, digest) - }; + let image_ref = { + let prefix = &file[img.full_range.start..digest.start]; + let digest = &response.headers()["docker-content-digest"].to_str().unwrap(); + format!("{}{}", prefix, digest) + }; - edits.push(Update{ - position: img.full_range, - content: image_ref, - }); - } + edits.push(Update{ + position: img.full_range, + content: image_ref, + }); } fn main() { |
