use http::HeaderMap;
use super::{Auth, AuthBuilder, AuthEvent, Result};
use crate::store::{Session, User};
use crate::types::{Access, AuthError, CloudflareConfig};
impl AuthBuilder {
#[cfg_attr(watchword_docsrs, doc(cfg(feature = "cloudflare")))]
pub fn cloudflare(mut self, config: Option<CloudflareConfig>) -> Self {
self.cloudflare = config;
self
}
}
impl Auth {
#[cfg_attr(watchword_docsrs, doc(cfg(feature = "cloudflare")))]
pub fn set_cloudflare(&self, config: Option<CloudflareConfig>) {
self.inner.cloudflare.set(config);
}
#[cfg_attr(watchword_docsrs, doc(cfg(feature = "cloudflare")))]
pub async fn cloudflare_available(&self, headers: &HeaderMap) -> Result<bool> {
Ok(self.cloudflare_user(headers).await?.is_some())
}
#[cfg_attr(watchword_docsrs, doc(cfg(feature = "cloudflare")))]
pub async fn sign_in_cloudflare(&self, headers: &HeaderMap) -> Result<(User, Session)> {
let mut user = self
.cloudflare_user(headers)
.await?
.ok_or(AuthError::Unauthorized)?;
if user.access == Access::Pending {
let activated = self.store().activate(user.id).await?;
user = self
.store()
.user(user.id)
.await?
.ok_or(AuthError::Unauthorized)?;
if activated {
tracing::info!(user = %user.id, "Cloudflare Access activated a pending user");
self.emit(AuthEvent::UserChanged(user.info()));
}
}
if user.access != Access::Active {
return Err(AuthError::Unauthorized.into());
}
let session = self.create_session(user.id).await?;
Ok((user, session))
}
async fn cloudflare_user(&self, headers: &HeaderMap) -> Result<Option<User>> {
let provider = &self.inner.cloudflare;
let Some(email) = provider.email(headers).await else {
return Ok(None);
};
let Some(user) = self.store().user_by_email(&email).await? else {
provider.warn(format!(
"Cloudflare Access sign-in failed: no user has the email {email}"
));
return Ok(None);
};
if user.access == Access::Revoked {
provider.warn(format!(
"Cloudflare Access sign-in failed: the user with the email {email} has had their access revoked"
));
return Ok(None);
}
Ok(Some(user))
}
#[cfg(test)]
pub(crate) fn cloudflare_warnings(&self) -> Vec<String> {
self.inner.cloudflare.warnings()
}
}
#[cfg(test)]
mod tests;