#Figuring out permissions in the fullstack auth example

1 messages · Page 1 of 1 (latest)

autumn ibex
lament wave
#

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?

autumn ibex
lament wave
autumn ibex
lament wave
autumn ibex
lament wave
autumn ibex
autumn ibex
#

Oh there is a panic

autumn ibex
#

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 ?

lament wave
autumn ibex
#

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
}
lament wave
#

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).

autumn ibex
#

It works! And there was no need for get_permissions() at all!

lament wave
#

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.)