github-mcp 0.1.0

GitHub v3 REST API MCP server, generated by mcpify.
Documentation
// GitHub v3 REST API MCP server — generated by mcpify. Do not hand-edit.

use async_trait::async_trait;
use base64::Engine;
use base64::engine::general_purpose::STANDARD;

use super::super::auth_strategy::{AuthConfig, AuthStrategy, Credentials};
use super::super::errors::AuthError;

#[derive(Debug, Default)]
pub struct BasicAuthStrategy;

#[async_trait]
impl AuthStrategy for BasicAuthStrategy {
    async fn authenticate(&self, config: &AuthConfig) -> anyhow::Result<Credentials> {
        let username = config
            .get("username")
            .ok_or_else(|| AuthError::MissingCredentials("username, password".to_string()))?;
        let password = config
            .get("password")
            .ok_or_else(|| AuthError::MissingCredentials("username, password".to_string()))?;

        let token = STANDARD.encode(format!("{username}:{password}"));
        let mut credentials = Credentials::new();
        credentials.insert("username".to_string(), username.clone());
        credentials.insert("password".to_string(), password.clone());
        credentials.insert("authorization_header".to_string(), format!("Basic {token}"));
        Ok(credentials)
    }

    fn validate_credentials(&self, credentials: &Credentials) -> bool {
        credentials.contains_key("authorization_header")
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn config(username: &str, password: &str) -> AuthConfig {
        AuthConfig::from([
            ("username".to_string(), username.to_string()),
            ("password".to_string(), password.to_string()),
        ])
    }

    #[tokio::test]
    async fn encodes_username_and_password_as_a_basic_authorization_header() {
        let strategy = BasicAuthStrategy;
        let credentials = strategy
            .authenticate(&config("alice", "s3cr3t"))
            .await
            .unwrap();
        assert_eq!(
            credentials.get("authorization_header").unwrap(),
            "Basic YWxpY2U6czNjcjN0"
        );
    }

    #[tokio::test]
    async fn rejects_a_config_missing_either_field() {
        let strategy = BasicAuthStrategy;
        assert!(strategy.authenticate(&AuthConfig::new()).await.is_err());
    }

    #[test]
    fn validates_credentials_carrying_an_authorization_header() {
        let strategy = BasicAuthStrategy;
        let credentials =
            Credentials::from([("authorization_header".to_string(), "Basic abc".to_string())]);
        assert!(strategy.validate_credentials(&credentials));
        assert!(!strategy.validate_credentials(&Credentials::new()));
    }
}