-
-
Notifications
You must be signed in to change notification settings - Fork 299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot access security scheme field, because it's private #915
Comments
Why Security Scheme must be public? Let's say we work on big project with many route. In order to make maintaining code easier and reduce git conflict we split route in to several module/file like this: use bar_route::BarRoute;
use foo_route::FooRoute;
use poem::{listener::TcpListener, Route};
use poem_openapi::OpenApiService;
mod foo_route {
use poem_openapi::{payload::PlainText, OpenApi};
pub struct FooRoute;
#[OpenApi]
impl FooRoute {
#[oai(path = "/foo", method = "get")]
async fn foo(&self) -> PlainText<String> {
PlainText("foo".to_string())
}
}
}
mod bar_route {
use poem_openapi::{payload::PlainText, OpenApi};
pub struct BarRoute;
#[OpenApi]
impl BarRoute {
#[oai(path = "/bar", method = "get")]
async fn foo(&self) -> PlainText<String> {
PlainText("bar".to_string())
}
}
}
#[tokio::main]
async fn main() {
let api_service = OpenApiService::new((FooRoute, BarRoute), "Authorization Demo", "1.0")
.server("http://localhost:3000/api");
let ui = api_service.swagger_ui();
let app = Route::new().nest("/api", api_service).nest("/", ui);
poem::Server::new(TcpListener::bind("0.0.0.0:3000"))
.run(app)
.await
.unwrap()
} In above example i use module foo and bar but In real world it can be module user, post etc. Although they are on different module/file but they use same authorization. So we add authorization like this: use bar_route::BarRoute;
use foo_route::FooRoute;
use poem::{listener::TcpListener, Route};
use poem_openapi::OpenApiService;
pub mod shared {
use poem::Request;
use poem_openapi::{auth::ApiKey, SecurityScheme};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct User {
pub username: String,
}
/// ApiKey authorization
#[derive(SecurityScheme)]
#[oai(
ty = "api_key",
key_name = "X-API-Key",
key_in = "header",
checker = "api_checker"
)]
pub struct MyApiKeyAuthorization(User);
pub async fn api_checker(_req: &Request, api_key: ApiKey) -> Option<User> {
Some(User {
username: api_key.key,
})
}
}
mod foo_route {
use poem_openapi::{payload::PlainText, OpenApi};
use crate::shared::MyApiKeyAuthorization;
pub struct FooRoute;
#[OpenApi]
impl FooRoute {
#[oai(path = "/foo", method = "get")]
async fn foo(&self, auth: MyApiKeyAuthorization) -> PlainText<String> {
println!("{:?}", auth.0.username); // Error due private field
PlainText("foo".to_string())
}
}
}
mod bar_route {
use poem_openapi::{payload::PlainText, OpenApi};
use crate::shared::MyApiKeyAuthorization;
pub struct BarRoute;
#[OpenApi]
impl BarRoute {
#[oai(path = "/bar", method = "get")]
async fn foo(&self, auth: MyApiKeyAuthorization) -> PlainText<String> {
println!("{:?}", auth.0.username); // Error due private field
PlainText("bar".to_string())
}
}
}
#[tokio::main]
async fn main() {
let api_service = OpenApiService::new((FooRoute, BarRoute), "Authorization Demo", "1.0")
.server("http://localhost:3000/api");
let ui = api_service.swagger_ui();
let app = Route::new().nest("/api", api_service).nest("/", ui);
poem::Server::new(TcpListener::bind("0.0.0.0:3000"))
.run(app)
.await
.unwrap()
} Code above will failed to compile due private field. It would be nice if we can put SecurityScheme in different file/module and use it in any file/module. |
Workaround that I found I try to find workaround. I inspect openapi schema generated by poem openapi. It's turn out, it set securitySchemes name using struct name "securitySchemes": {
"MyApiKeyAuthorization": {
"type": "apiKey",
"description": "ApiKey authorization",
"name": "X-API-Key",
"in": "header"
}
} So by define SecuritySchema using struct with the same name, Openapi will registered it as same authorization, Although we define it more than once. Here my full solution: use bar_route::BarRoute;
use foo_route::FooRoute;
use poem::{listener::TcpListener, Route};
use poem_openapi::OpenApiService;
mod foo_route {
use poem::Request;
use poem_openapi::{auth::ApiKey, payload::PlainText, OpenApi, SecurityScheme};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct User {
pub username: String,
}
/// ApiKey authorization
#[derive(SecurityScheme)]
#[oai(
ty = "api_key",
key_name = "X-API-Key",
key_in = "header",
checker = "api_checker"
)]
pub struct MyApiKeyAuthorization(User); // <- Make sure it's has same name with bar_route SecurityScheme
pub async fn api_checker(_req: &Request, api_key: ApiKey) -> Option<User> {
Some(User {
username: api_key.key,
})
}
pub struct FooRoute;
#[OpenApi]
impl FooRoute {
#[oai(path = "/foo", method = "get")]
async fn foo(&self, auth: MyApiKeyAuthorization) -> PlainText<String> {
println!("{:?}", auth.0.username);
PlainText("foo".to_string())
}
}
}
mod bar_route {
use poem::Request;
use poem_openapi::{auth::ApiKey, payload::PlainText, OpenApi, SecurityScheme};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct User {
pub username: String,
}
/// ApiKey authorization
#[derive(SecurityScheme)]
#[oai(
ty = "api_key",
key_name = "X-API-Key",
key_in = "header",
checker = "api_checker"
)]
pub struct MyApiKeyAuthorization(User); // <- Make sure it's has same name with foo_route SecurityScheme
pub async fn api_checker(_req: &Request, api_key: ApiKey) -> Option<User> {
Some(User {
username: api_key.key,
})
}
pub struct BarRoute;
#[OpenApi]
impl BarRoute {
#[oai(path = "/bar", method = "get")]
async fn foo(&self, auth: MyApiKeyAuthorization) -> PlainText<String> {
println!("{:?}", auth.0.username);
PlainText("bar".to_string())
}
}
}
#[tokio::main]
async fn main() {
let api_service = OpenApiService::new((FooRoute, BarRoute), "Authorization Demo", "1.0")
.server("http://localhost:3000/api");
let ui = api_service.swagger_ui();
let app = Route::new().nest("/api", api_service).nest("/", ui);
poem::Server::new(TcpListener::bind("0.0.0.0:3000"))
.run(app)
.await
.unwrap()
} |
I try openapi example on how to use auth-api key auth api openapi example.
It's work like a charm no error. Then I try to move authorization struct to different module/file like this:
then I got this error
It's say it's private field but I already set struct and it's field as public. How to move Authorization struct to different module/file without error and access it's field value?
I use rust version
rustc 1.82.0 (f6e511eec 2024-10-15)
and here my depedenciesThe text was updated successfully, but these errors were encountered: