1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use http::{HeaderMap, HeaderValue};
use log::error;
use thiserror::Error;
use crate::api::job::Job;
use crate::api::users::CurrentUser;
use crate::api::{self, AsyncQuery, Query};
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum AuthError {
#[error("header value error: {}", source)]
HeaderValue {
#[from]
source: http::header::InvalidHeaderValue,
},
}
type AuthResult<T> = Result<T, AuthError>;
/// A Gitlab API token
///
/// Gitlab supports two kinds of tokens
#[derive(Clone)]
#[non_exhaustive]
pub enum Auth {
/// A personal access token, obtained through Gitlab user settings
Token(String),
/// A job token, obtained through a CI job.
JobToken(String),
/// An OAuth2 token, obtained through the OAuth2 flow
OAuth2(String),
/// Unauthenticated access
None,
}
impl Auth {
/// Adds the appropriate header to a set of headers.
///
/// Depending on the token type, this will be either the Private-Token header
/// or the Authorization header.
///
/// Returns an error if the token string cannot be parsed as a header value.
pub fn set_header<'a>(
&self,
headers: &'a mut HeaderMap<HeaderValue>,
) -> AuthResult<&'a mut HeaderMap<HeaderValue>> {
match self {
Auth::Token(token) => {
let mut token_header_value = HeaderValue::from_str(token)?;
token_header_value.set_sensitive(true);
headers.insert("PRIVATE-TOKEN", token_header_value);
},
Auth::JobToken(token) => {
let mut token_header_value = HeaderValue::from_str(token)?;
token_header_value.set_sensitive(true);
headers.insert("JOB-TOKEN", token_header_value);
},
Auth::OAuth2(token) => {
let value = format!("Bearer {}", token);
let mut token_header_value = HeaderValue::from_str(&value)?;
token_header_value.set_sensitive(true);
headers.insert(http::header::AUTHORIZATION, token_header_value);
},
Auth::None => {},
}
Ok(headers)
}
pub fn check_connection<C>(&self, api: &C) -> Result<(), api::ApiError<C::Error>>
where
C: api::Client,
{
match self {
Self::None => {
// There does not seem to be an unparameterized endpoint that can be used to reliably
// detect whether the connection will work or not.
},
Self::JobToken(_) => {
api::ignore(Job::builder().build().unwrap()).query(api)?;
},
Self::Token(_) | Self::OAuth2(_) => {
api::ignore(CurrentUser::builder().build().unwrap()).query(api)?;
},
}
Ok(())
}
pub async fn check_connection_async<C>(&self, api: &C) -> Result<(), api::ApiError<C::Error>>
where
C: api::AsyncClient + Sync,
{
match self {
Self::None => {
// There does not seem to be an unparameterized endpoint that can be used to reliably
// detect whether the connection will work or not.
},
Self::JobToken(_) => {
api::ignore(Job::builder().build().unwrap())
.query_async(api)
.await?;
},
Self::Token(_) | Self::OAuth2(_) => {
api::ignore(CurrentUser::builder().build().unwrap())
.query_async(api)
.await?;
},
}
Ok(())
}
}