openfga_client/
lib.rs

1#![warn(
2    missing_debug_implementations,
3    rust_2018_idioms,
4    unreachable_pub,
5    clippy::pedantic
6)]
7#![forbid(unsafe_code)]
8
9//! # OpenFGA Rust Client
10//!
11//! [![Crates.io](https://img.shields.io/crates/v/openfga-client)](https://crates.io/crates/openfga-client)
12//! [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
13//! [![Tests](https://github.com/vakamo-labs/openfga-client/actions/workflows/ci.yaml/badge.svg)](https://github.com/vakamo-labs/openfga-client/actions/workflows/ci.yaml)
14//!
15//! OpenFGA Rust Client is a type-safe client for OpenFGA with optional Authorization Model management and Authentication (Bearer or Client Credentials).
16//!
17//! ## Features
18//!
19//! * No dependency on `protoc` - Rust files are pre-generated.
20//! * Type-safe client for OpenFGA (gRPC) build on `tonic`
21//! * (JSON) Serialization and deserialization for Authorization Models in addition go protobuf Messages
22//! * Optional Authorization Model management with Migration hooks if tuples need to be re-written. Ideal for stateless deployments. State is managed exclusively in OpenFGA. This enables fully automated model management by your Application without blindly re-writing of Authorization Models on startup!
23//! * Optional Authentication (Bearer or Client Credentials) via the [Middle Crate](https://crates.io/crates/middle). (Feature: `auth-middle`)
24//! * Convenience functions like `read_all_tuples` (handles pagination), `get_store_by_name` and more.
25//!
26//! # Usage
27//!
28//! ## Basic Usage
29//! ```no_run
30//! use openfga_client::client::OpenFgaServiceClient;
31//! use tonic::transport::Channel;
32//!
33//! #[tokio::main]
34//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
35//!     let endpoint = "http://localhost:8080";
36//!     let client = OpenFgaServiceClient::connect(endpoint).await?;
37//!
38//!     // Use the client to interact with OpenFGA
39//!     Ok(())
40//! }
41//! ```
42//!
43//! ## Bearer Token Authentication (API-Key)
44//! ```no_run
45//! use openfga_client::client::BasicOpenFgaServiceClient;
46//!
47//! fn main() -> Result<(), Box<dyn std::error::Error>> {
48//!     let endpoint = "http://localhost:8080";
49//!     let token = "your-bearer-token";
50//!     let client = BasicOpenFgaServiceClient::new_with_basic_auth(endpoint, token)?;
51//!
52//!     // Use the client to interact with OpenFGA
53//!     Ok(())
54//! }
55//! ```
56//!
57//! ## Client Credential Authentication
58//! ```no_run
59//! use openfga_client::client::BasicOpenFgaServiceClient;
60//! use url::Url;
61//!
62//! #[tokio::main]
63//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
64//!     let endpoint = "http://localhost:8080";
65//!     let client_id = "your-client-id";
66//!     let client_secret = "your-client-secret";
67//!     let token_endpoint = Url::parse("http://localhost:8080/token")?;
68//!     let client = BasicOpenFgaServiceClient::new_with_client_credentials(endpoint, client_id, client_secret, &token_endpoint).await?;
69//!
70//!     // Use the client to interact with OpenFGA
71//!     Ok(())
72//! }
73//! ```
74//!
75//! ## License
76//! This project is licensed under the Apache-2.0 License. See the LICENSE file for details.
77//!
78//! ## Contributing
79//! Contributions are welcome! Please open an issue or submit a pull request on GitHub.
80
81pub use prost_types;
82pub use tonic;
83pub mod error;
84pub mod migration;
85
86mod client_ext;
87mod conversions;
88
89mod generated {
90    #![allow(clippy::all)]
91    #![allow(clippy::pedantic)]
92
93    include!("gen/openfga.v1.rs");
94}
95
96pub mod client {
97    pub use open_fga_service_client::OpenFgaServiceClient;
98
99    #[cfg(feature = "auth-middle")]
100    pub use super::client_ext::BasicOpenFgaServiceClient;
101    pub use super::generated::*;
102}
103
104#[cfg(test)]
105mod test_json_serde {
106    use super::client::*;
107
108    fn test_authorization_model_serde(schema: &str) {
109        let schema_json: serde_json::Value = schema.parse().unwrap();
110        let schema: AuthorizationModel = serde_json::from_value(schema_json.clone()).unwrap();
111        let value = serde_json::to_value(&schema).unwrap();
112        assert_eq!(schema_json, value);
113    }
114    #[test]
115    fn test_serde_custom_roles() {
116        test_authorization_model_serde(include_str!(
117            "../tests/sample-store/custom-roles/schema.json"
118        ));
119    }
120
121    #[test]
122    fn test_serde_entitlements() {
123        test_authorization_model_serde(include_str!(
124            "../tests/sample-store/entitlements/schema.json"
125        ));
126    }
127
128    #[test]
129    fn test_serde_expenses() {
130        test_authorization_model_serde(include_str!("../tests/sample-store/expenses/schema.json"));
131    }
132
133    // gdrive, github, iot, issue-tracker, modular, slack
134    #[test]
135    fn test_serde_gdrive() {
136        test_authorization_model_serde(include_str!("../tests/sample-store/gdrive/schema.json"));
137    }
138
139    #[test]
140    fn test_serde_github() {
141        test_authorization_model_serde(include_str!("../tests/sample-store/github/schema.json"));
142    }
143
144    #[test]
145    fn test_serde_iot() {
146        test_authorization_model_serde(include_str!("../tests/sample-store/iot/schema.json"));
147    }
148
149    #[test]
150    fn test_serde_modular() {
151        test_authorization_model_serde(include_str!("../tests/sample-store/modular/schema.json"));
152    }
153
154    #[test]
155    fn test_serde_slack() {
156        test_authorization_model_serde(include_str!("../tests/sample-store/slack/schema.json"));
157    }
158}