#actix-web #permissions #input-validation #authorization #web-services #actix #service-request

actix-permissions

Permission and input validation extension for Actix Web. Alternative to actix guard, with access to HttpRequest and Payload.

11 releases (4 stable)

2.0.0 Apr 12, 2022
2.0.0-beta.4 Apr 11, 2022
1.0.2 Apr 8, 2022
1.0.1 Mar 29, 2022
0.1.1 Mar 8, 2022

#1288 in Web programming

49 downloads per month

MIT/Apache

26KB
428 lines

Actix Permissions Continuous Integration cargo-badge license-badge

Permission and input validation extension for Actix Web. Alternative to actix guard, with access to app data injections, HttpRequest and Payload. Permissions are flexible, take a look at Examples directory for some of the use cases.

You could write a permission check like a function or like a struct.
This code:

async fn is_allowed(
    req: HttpRequest
) -> actix_web::Result<bool> {
    todo!();
}

is almost the same as writing:

struct IsAllowed;

impl Permission<()> for IsAllowed {
    type Future = Ready<actix_web::Result<bool>>;
    
    fn call(&self, req: HttpRequest, ctx: ()) -> Ready<actix_web::Result<bool>> {
        todo!();
    }
}

Example

Dependencies:

[dependencies]
actix-permissions = "2.0.0"

Code:

use actix_permissions::*;
use actix_web::web::Data;
use actix_web::*;
use serde::Deserialize;

#[derive(Debug, Clone, Deserialize)]
pub struct MyStatus {
    pub status: Option<String>,
}

async fn dummy_permission_check(
    _req: HttpRequest,
    dummy_service: web::Data<DummyService>,
    data: web::Query<MyStatus>,
) -> actix_web::Result<bool> {
    // Unecessary complicating permission check to show what it can do.
    // You have access to request, payload, and all injected dependencies through app_data.
    Ok(dummy_service.check(data.status.clone()))
}

struct DummyService;

impl DummyService {
    pub fn check(&self, value: Option<String>) -> bool {
        value == Some("all".to_string())
    }
}

async fn index() -> Result<String, Error> {
    Ok("Hi there!".to_string())
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new().app_data(Data::new(DummyService)).service(
            web::scope("").route(
                "/",
                permission()
                    .check(web::get())
                    .validate(dummy_permission_check)
                    .to(index)
                    .build(),
            ),
        )
    })
        .bind("127.0.0.1:8888")?
        .run()
        .await
}

Use Cases

Take a look at Examples directory. You could use actix-permissions for role based authorization check, like in role-based-authorization example.
hello-world example is just a proof of concept, showing how you can set permission, access service request, payload and injected services.

Permission Deny

By default, 403 is returned for failed permission checks. You may want to toggle between Unauthorized and Forbidden, maybe customize 403 forbidden messages. That's why check_with_custom_deny is for. Take a look at role based authorization example for more info.

Permission chaining

Not implemented more than one. It's because how it's implemented in service.rs. For every Permission there's a FromRequest impl. Every FromRequest consumes Payload, so Payload needs to be cloned each time. If you have a need for permission chaining or have an idea, open a new Issue.

Contributing

This project welcomes all kinds of contributions. No contribution is too small!

If you want to contribute to this project but don't know how to begin or if you need help with something related to this project, feel free to send me an email https://www.eisberg-labs.com/ (contact form at the bottom).

Some pointers on contribution are in Contributing.md

Code of Conduct

This project follows the Rust Code of Conduct.

License

Distributed under the terms of MIT license and Apache license.

Dependencies

~14–25MB
~436K SLoC