This will do:
- If both username/password are empty, does not require to enter username/password when access the website.
- If any of username/password are not empty, require user to enter username/password to access the website.
Assume username/password are stored in a struct named Config.
#[derive(Clone)]
struct Config {
basic_auth_user: String,
basic_auth_pass: String,
}
Create a function to validate the entered username/password:
async fn basic_auth_validator(
req: ServiceRequest,
credentials: BasicAuth,
) -> Result<ServiceRequest, (Error, ServiceRequest)> {
let mut user_ok = false;
let mut pass_ok = false;
if let Some(config) = req.app_data::<Data<Config>>() {
user_ok =
config.basic_auth_user.is_empty() || credentials.user_id() == config.basic_auth_user;
pass_ok = config.basic_auth_pass.is_empty()
|| credentials.password() == Some(config.basic_auth_pass.as_str());
}
if user_ok && pass_ok {
Ok(req)
} else {
let resp = HttpResponse::Unauthorized()
.insert_header((header::WWW_AUTHENTICATE, r#"Basic realm="Restricted""#))
.finish();
let err = InternalError::from_response("Unauthorized", resp).into();
Err((err, req))
}
}
Add the validator to actix App:
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Config should be parsed from config file or from database
let config = Config {
basic_auth_user: "admin".to_string(),
basic_auth_pass: "admin".to_string(),
};
let config_data = Arc::new(config.clone());
HttpServer::new(move || {
// If username/password are empty, do not enable basic auth
let enable_basic_auth =
!config.basic_auth_user.is_empty() || !config.basic_auth_pass.is_empty();
App::new()
.wrap(Condition::new(
enable_basic_auth,
HttpAuthentication::basic(basic_auth_validator),
))
.app_data(Data::from(config_data.clone()))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Comments