I can't figure out how to use the function get_permissions() to restrict access to certain areas of a site, which is written starting at line 142 at https://github.com/DioxusLabs/dioxus/blob/7102bc3b6a0ddea3a9e71423fc6d667df8d956f3/examples/07-fullstack/auth/src/main.rs can someone tell me a bit about it?
#Figuring out permissions in the fullstack auth example
1 messages · Page 1 of 1 (latest)
The example is using some axum_session* crates, so I'd start with getting familiar with those to better understand the example. But, if I'm understanding your question correctly, you'd use the get_permissions fn to hide unauthorized areas of the UI and have similar access control logic found in that endpoint to do the actual restriction for the data loaded through other endpoints. Does that help?
What I have been trying so far, is to check for permissions, and if there was none then redirect to the login
That sounds right (assuming the check is happening on the server for access control). Do you have an example of the approach you're taking and what you need help with?
i am calling get_permissions() in the component itself is that the right approach? I can have an example of what I am doing tomorrow 🙂
Yes, on the client that's a viable approach for preventing users from encountering authorization errors, but without access control on the server (e.g. calls to Auth::build().validate() with endpoint specifics) anyone can enjoy full access through the server API 😉
ah! I will have to read up on this
Best of luck! Let me know if you need any additional pointers
Thank you so much! 🙏
Oh there is a panic
So I've managed to implement the middleware the checks if the get_permissions()is Ok - but don't see how to get it to depend on the path, because if I try to define
#[get("/api/user/permissions", auth: backend::Session)]
pub async fn get_permissions(path: &str) -> Result<HashSet<String>> {
use axum_session_auth::{Auth, Rights};
use backend::User;
let user = auth.current_user.unwrap();
Auth::<User, i64, sqlx::SqlitePool>::build([axum::http::Method::GET], false)
.requires(Rights::any([
// Rights::permission("Category::View"),
Rights::permission("User::Admin"),
]))
.validate(&user, &axum::http::Method::GET, None)
.await
.or_unauthorized("You do not have permission to view categories")?;
Ok(user.permissions)
}
I get the following compiler error:
[cargo] error: implementation of `Deserialize` is not general enough
--> src/main.rs:222:1
|
222 | #[get("/api/user/permissions", auth: backend::Session)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Deserialize` is not general enough
|
= note: `get_permissions::{closure#0}::___Body_Serialize___<&str>` must implement `Deserialize<'0>`, for any lifetime `'0`...
= note: ...but it actually implements `Deserialize<'1>`, for some specific lifetime `'1`
= note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info)
Do you have any suggestions @lament wave ?
Hmm... What does backend::Session look like? Is that still a type alias for axum_session_auth::AuthSession? And are you trying to call get_permissions() on the backend?
it's
pub(crate) type Session = axum_session_auth::AuthSession<User, i64, SessionSqlitePool, SqlitePool>;
I am using the middleware
pub async fn auth_check(
auth_session: Session, // New axum extractor for our session
request: Request,
next: Next,
) -> Response {
let mut permissions: HashMap<&str, Rights> = HashMap::new();
permissions.insert("/admin", Rights::permission("User::Admin"));
permissions.insert("/home", Rights::permission("User::Regular"));
let path = request.uri().path();
let is_unsecured_route = is_unsecured_route(&path)
|| is_unsecured_endpoint(&path)
|| path.starts_with("/wasm/") // IMPORTANT; not allowing this will break any dioxus hooks and stop your website from working
|| path.starts_with("/assets/");
if !is_unsecured_route && !auth_session.is_authenticated() {
info!("Not authenticated 2");
return Redirect::to(&Route::Login {}.to_string()).into_response();
}
if !is_unsecured_route && auth_session.is_authenticated() {
info!("Authenticated");
if !get_permissions().await.is_ok() {
return Redirect::to(&Route::Deny {}.to_string()).into_response();
}
}
next.run(request).await
}
Okay, I'm not sure if I'm getting the whole picture, but you'll want to do the Auth::build().requires().validate()... from within auth_check (or another server-only fn it calls) and remove that from get_permissions (assuming you still plan on calling that from the frontend for UX purposes, any required permissions a user needs to see their own permissions would be specified in your middleware).
Thsnks! I will do this 🙏
It works! And there was no need for get_permissions() at all!
Yay! You may want to also consider the trade-offs you're making by using the middleware vs calling a helper from each endpoint fn; e.g. If you add a new endpoint and forget to add it to auth_check, will it default allow or deny? And if you come back to the codebase after forgetting how everything works, how easy will it be to determine if an endpoint your looking at requires authentication or authorization? (There are other options too, such as specifying permissions in a proc-macro attached to each endpoint, but idk if there's an easy enough path to getting there.)