Учебное пособие по интеграции Casbin с веб-сервисами Poem


Введение

Casbin-rs — это библиотека авторизации, поддерживающая такие модели управления доступом, как ACL, RBAC, ABAC, написанная на языке Rust.

Poem — это полнофункциональный и простой в использовании веб-фреймворк с языком программирования Rust.

В этом руководстве мы будем интегрировать casbin-rs с веб-сервисами poem, используя промежуточное ПО poem-casbin.

casbin-rs / poem-casbin

Промежуточное программное обеспечение для контроля доступа Casbin Poem

Поэма Casbin Middleware

Промежуточное ПО управления доступом Casbin для фреймворка poem

Установите .

Добавьте его в Cargo.toml

poem = "1.3.31"
poem-casbin-auth = "0.x.x"
tokio = { version = "1.17.0", features = ["rt-multi-thread", "macros"] }
Войдите в полноэкранный режим Выйти из полноэкранного режима

Требование

Casbin берет на себя только контроль разрешений, поэтому вам необходимо реализовать Authentication Middleware для идентификации пользователя.

Вы должны поместить poem_casbin_auth::CasbinVals, который содержит subject(имя пользователя) и domain(опционально) в Extension.

Например:

use poem::{
    Endpoint, EndpointExt, Middleware, Request, Result,
};
use poem_casbin_auth::CasbinVals;
pub struct FakeAuth;

pub struct FakeAuthMiddleware<E> {
    ep: E,
}

impl<E: Endpoint> Middleware<E> for FakeAuth {
    type Output = FakeAuthMiddleware<E>;

    fn transform(&self, ep: E)

Войти в полноэкранный режим Выход из полноэкранного режима
Посмотреть на GitHub

Напишите службу hello-world с помощью стихотворения

Сначала создайте крейт cargo, затем добавьте следующие зависимости в Cargo.toml:

tokio = { version = "1.20.0", features = ["rt-multi-thread", "macros"] }
poem = "1.3.35"
Войти в полноэкранный режим Выход из полноэкранного режима

Добавьте следующий код в main.rs.

use poem::{get, handler, listener::TcpListener, web::Path, Route, Server};
use std::env;

#[handler]
fn pen1() -> String {
    String::from("I'm pen 1")
}

#[handler]
fn pen2() -> String {
    String::from("I'm pen 2")
}

#[handler]
fn book(Path(id): Path<String>) -> String {
    format!("I'm book {}", id)
}

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
    if env::var_os("RUST_LOG").is_none() {
        env::set_var("RUST_LOG", "poem=debug");
    }
    let app = Route::new()
        .at("/pen/1", get(pen1))
        .at("/pen/2", get(pen2))
        .at("/book/:id", get(book));
    Server::new(TcpListener::bind("127.0.0.1:3000"))
        .name("poem-casbin-demo")
        .run(app)
        .await
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Есть 3 конечные точки, /pen/1, /pen/2, и /book/:id. Все довольно просто, верно? Давайте запустим наш сервис, введите cargo run и наш сервис будет доступен по адресу 127.0.0.1:3000.

Давайте используем curl для тестирования нашего сервиса:

Интеграция с базовым промежуточным программным обеспечением auth

Обратите внимание, что casbin-poem — это промежуточное ПО авторизации, а не аутентификации. Casbin отвечает только за контроль разрешений, поэтому нам необходимо реализовать промежуточное ПО аутентификации для идентификации пользователя.

В этой части мы интегрируем базовое промежуточное ПО аутентификации в наш сервис.

Для начала добавьте следующую зависимость в Cargo.toml:

poem-casbin-auth = { git = "https://github.com/casbin-rs/poem-casbin.git" }
Вход в полноэкранный режим Выйти из полноэкранного режима

Затем создайте файл с именем auth.rs и добавьте в него следующий код:

use poem::{
    http::StatusCode,
    web::{
        headers,
        headers::{authorization::Basic, HeaderMapExt},
    },
    Endpoint, Error, Middleware, Request, Result,
};
use poem_casbin_auth::CasbinVals;

pub struct BasicAuth;

impl<E: Endpoint> Middleware<E> for BasicAuth {
    type Output = BasicAuthEndpoint<E>;

    fn transform(&self, ep: E) -> Self::Output {
        BasicAuthEndpoint { ep }
    }
}

pub struct BasicAuthEndpoint<E> {
    ep: E,
}

#[poem::async_trait]
impl<E: Endpoint> Endpoint for BasicAuthEndpoint<E> {
    type Output = E::Output;

    async fn call(&self, mut req: Request) -> Result<Self::Output> {
        if let Some(auth) = req.headers().typed_get::<headers::Authorization<Basic>>() {
            let vals = CasbinVals {
                subject: String::from(auth.username()),
                domain: None,
            };
            req.extensions_mut().insert(vals);
            self.ep.call(req).await
        } else {
            Err(Error::from_status(StatusCode::UNAUTHORIZED))
        }
    }
}
Вход в полноэкранный режим Выйти из полноэкранного режима

В этом моде мы реализуем базовый auth middleware, для простоты, здесь мы не проверяем имя пользователя и пароль, вместо этого мы просто вставляем CasbinVals с предоставленным именем пользователя в Extension, так что poem-casbin middleware может извлечь информацию об идентификации. Если запрос не имеет basic auth, то промежуточное ПО вернет 401 Unauthorized.

Затем давайте интегрируем это промежуточное ПО basic auth с нашим сервисом. Во-первых, добавьте следующий код в main.rs:

mod auth;

use poem_casbin_auth::CasbinVals;
Вход в полноэкранный режим Выход из полноэкранного режима

Затем добавьте новый обработчик для подтверждения того, что наше промежуточное ПО auth правильно вставило информацию об идентификации:

#[handler]
fn user(data: Data<&CasbinVals>) -> String {
    format!("Hello, {}", &data.subject)
}
Вход в полноэкранный режим Выход из полноэкранного режима

Наконец, перепишите функцию main, чтобы добавить конечную точку /user и оберните все конечные точки базовым промежуточным ПО auth, теперь это выглядит так:

let app = Route::new()
        .at("/pen/1", get(pen1))
        .at("/pen/2", get(pen2))
        .at("/book/:id", get(book))
        .at("/user", get(user))
        .with(casbin_middleware)
        .with(auth::BasicAuth);
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь давайте снова используем curl для тестирования нашего сервиса.

Как вы видите, если мы не предоставим basic auth при доступе к нашему сервису, мы получим 401 Unauthorized. Наш запрос будет прерван промежуточным ПО basic auth. Давайте отправлять запросы с помощью basic auth:

curl -u alice:123 localhost:3000/book/1
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь мы можем получить ответ как обычно. Похоже, что наше промежуточное ПО basic auth работает хорошо.

Интеграция с промежуточным ПО poem-casbin

В последней части мы интегрируем промежуточное ПО poem-casbin с нашим сервисом.

Во-первых, нам нужно предоставить файлы conf и policy в корневой директории проекта.

rbac_with_pattern_model.conf выглядит следующим образом:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _
g2 = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && g2(r.obj, p.obj) && regexMatch(r.act, p.act)
Войти в полноэкранный режим Выйти из полноэкранного режима

rbac_with_pattern_policy.csv выглядит как:

p, alice, /pen/1, GET
p, book_admin, book_group, GET
p, pen_admin, pen_group, GET
,,,
g, alice, book_admin,
g, bob, pen_admin,
g2, /book/:id, book_group,
g2, /pen/:id, pen_group,
Войти в полноэкранный режим Выйти из полноэкранного режима

Эта политика означает:

  • Для Алисы
    • может получить доступ к /pen/1
    • является book_admin, поэтому может получить доступ к /book/:id
  • Для Боба:
    • является pen_admin, поэтому может получить доступ к /pen/:id.

Теперь давайте сосредоточимся на main.rs, сначала добавим в него следующий код:

use poem_casbin_auth::casbin::function_map::key_match2;
use poem_casbin_auth::casbin::{CoreApi, DefaultModel, FileAdapter};
use poem_casbin_auth::{CasbinService, CasbinVals};
Вход в полноэкранный режим Выход из полноэкранного режима

Затем перепишите функцию main, чтобы обернуть наш сервис с помощью промежуточного ПО poem-casbin:

let m = DefaultModel::from_file("rbac_with_pattern_model.conf")
        .await
        .unwrap();
let a = FileAdapter::new("rbac_with_pattern_policy.csv");

let casbin_middleware = CasbinService::new(m, a).await.unwrap();

casbin_middleware
        .write()
        .await
        .get_role_manager()
        .write()
        .matching_fn(Some(key_match2), None);

let app = Route::new()
        .at("/pen/1", get(pen1))
        .at("/pen/2", get(pen2))
        .at("/book/:id", get(book))
        .at("/user", get(user))
        .with(casbin_middleware)
        .with(auth::BasicAuth);
Войти в полноэкранный режим Выйти из полноэкранного режима

Здесь мы сначала читаем conf и политику, затем создаем casbin_middleware и изменяем matching_fn на key_match для соответствия пути подстановочного символа (например, /:id). И наконец, мы оборачиваем все конечные точки casbin_middleware.

Вот и вся работа, которую мы должны сделать, чтобы интегрировать промежуточное ПО poem-casbin в наш сервис, довольно просто, не так ли?

Опять же, давайте используем curl для тестирования нашего сервиса:

Если alice захочет получить доступ к /pen/2, она получит 403 Forbidden, потому что ей не разрешен доступ к этой конечной точке.

Аналогично, Боб не может получить доступ к /book/2:

Все в порядке, когда оба пользователя посылают запросы к конечным точкам, к которым они могут получить доступ:

Резюме

В этом руководстве мы написали веб-сервис hello-world, используя poem, затем интегрировали в него базовый auth и промежуточное ПО casbin-poem. Это довольно простой проект, состоящий всего из ~100 LOC, его код можно найти в этом репозитории:

greenhandatsjtu / poem-casbin-demo

Демонстрация интеграции casbin-rs с веб-сервисами poem с помощью промежуточного ПО poem-casbin

poem-casbin-demo

Демонстрация интеграции casbin-rs с веб-сервисами poem с использованием промежуточного ПО poem-casbin

Введение

Casbin-rs — это библиотека авторизации, поддерживающая такие модели управления доступом, как ACL, RBAC, ABAC, написанная на языке Rust.

Poem — это полнофункциональный и простой в использовании веб-фреймворк с языком программирования Rust.

В этом руководстве мы будем интегрировать casbin-rs с веб-сервисами poem, используя промежуточное ПО poem-casbin.

Написание сервиса hello-world с помощью poem

Сначала создайте крейт cargo, затем добавьте следующие зависимости в Cargo.toml:

tokio = { version = "1.20.0", features = ["rt-multi-thread", "macros"] }
poem = "1.3.35"
Войти в полноэкранный режим Выход из полноэкранного режима

Добавьте следующий код в main.rs.

use poem::{get, handler, listener::TcpListener, web::Path, Route, Server};
use std::env;
#[handler]
fn pen1() -> String {
    String::from("I'm pen 1"

Войти в полноэкранный режим Выйти из полноэкранного режима
Посмотреть на GitHub

Оцените статью
devanswers.ru
Добавить комментарий