A strong, compile-time enforced authorization framework for rust applications.

Overview

DACquiri

A compile-time enforced authorization framework for Rust applications.

Authorization

In typical applications, authorization checks are performed in potentially random segments of the code. This leads to implicit assumptions on what kinds of permissions or checks have been enforced at various parts of the codebase. For example:

fn handler(req: Request) -> Result<Response, Error> {
  privileged_fn(req.get_user())
}

// makes no assumptions on a user's permissions or access, initially
fn privileged_fn(user: User) -> Result<Response, Error> {
  if !user.has(SimplePermission) { return Err(Error::PermissionError); }
  
  // action
  other_privileged_fn(user)
}

// Implicitly depends on user having the "SimplePermissions" permission or role. 
fn other_privileged_fn(user: User) -> Result<Response, Error> {
  if !user.has(AdvancedPermission) { return Err(Error::PermissionError); }
  
  // other action
  Ok(())
}

DACquiri does things differently.

With DACquiri, you explicitly declare your authorization requirements in the function definition. DACquiri will, at compile-time, enforce all code-paths invoking your function will have checked the appropriate authorization requirements beforehand.

With DACquiri, you:

  • Know all of your authorization requirements based on your function's definition
  • Know that all authorization requirements are enforced in all codepaths
  • Know that authorization violations cannot be introduced accidentally

Missing an authorization check? That's a compile-time error.

Missing DACquiri? That's your error.

How it works

DACquiri codifies permissions checks into the type system using a wrapper struct called GrantChain. For example, let's imagine you have two permissions called P1 and P2. If you've checked both of these permissions on some User object, you might expect to now have a type GrantChain<P2, GrantChain<P1, User>>.

The magic of DACquiri is that it doesn't matter in which order you check permissions, just that you've checked them at some point. Regardless of the order, the outer GrantChain will implement both HasGrant<P1> as well as HasGrant<P2>. This is true for no matter how many grants you add to the chain.

Grants can be checked with the try_grant function where you'll specify which Grant you are currently checking. The actual check is performed in the has_grant function you must implement when implementing Grant on your PrincipalT.

Example

Here's a simplistic example of two permissions (PermissionOne and PermissionTwo) that we'll define as being grantable to all User objects (for the sake of this example).

use dacquiri::prelude::*;

impl_principal!(User);
struct User {
    name: String
}

struct PermissionOne;
struct PermissionTwo;

impl Grant for PermissionOne {
    type Principal = User;

    // give everyone this grant
    fn check_grant(_: &Self::Principal, _: &Self::Resource) -> Result<(), String> { Ok(()) }
    fn new_with_resource(_: Self::Resource) -> Self { Self }
    fn get_resource(&self) -> &Self::Resource { &() }
}

impl Grant for PermissionTwo {
    type Principal = User;

    // give everyone this grant
    fn check_grant(_: &Self::Principal, _: &Self::Resource) -> Result<(), String> { Ok(()) }
    fn new_with_resource(_: Self::Resource) -> Self { Self }
    fn get_resource(&self) -> &Self::Resource { &() }
}

fn requires_permission_one(caller: &impl HasGrant<PermissionOne>) {
    println!("The caller must have checked that you have PermissionOne");
}

fn requires_permission_two(caller: &impl HasGrant<PermissionTwo>) {
    println!("The caller must have checked that you have PermissionTwo");
}

fn requires_both_permission(
    caller: impl HasGrant<PermissionOne>
               + HasGrant<PermissionTwo>
) {
    println!("The caller must have checked that you had both PermissionOne and PermissionTwo");
}

fn main() -> Result<(), String> {
    let user = User { name: format!("d0nut") };
    
    let p1_user = user.try_grant::<PermissionOne, _>(())?;
    requires_permission_one(&p1_user);

    let p2_user = p1_user.try_grant::<PermissionTwo, _>(())?;
    requires_permission_two(&p2_user);

    requires_both_permission(p2_user);
}
Comments
  • Add support for generic attributes

    Add support for generic attributes

    Support generic attributes so both a User and a Team can be Enabled.

    e.g.

    #[attribute(Enabled)]
    fn check_team_is_enabled(team: &Team) -> AttributeResult<Error> {
        // ...
    }
    
    #[attribute(Enabled)]
    fn check_user_is_enabled(user: &User) -> AttributeResult<Error> {
        // ...
    }
    

    Expected that Enabled carries generics with it to identify the 'subject' and 'resource' type.

    opened by d0nutptr 0
  • Add support for OR-based logic

    Add support for OR-based logic

    1. Should allow for OR-based logic in policy definition
    2. Should allow as much inheritance of constraints as possible
      1. Should likely avoid supporting Policies in multi-guard policy definition
    opened by d0nutptr 0
  • Add support for generic policies

    Add support for generic policies

    Similar to generic attributes, policies typically require concrete types defined in the entities. While this works fine for types we know ahead of time, if we begin supporting generic attributes then a natural question will be "can we support generic policies for types we don't about yet?"

    This has not be validated to be feasible yet.

    opened by d0nutptr 0
  • Add support for generic attributes

    Add support for generic attributes

    Currently, attributes require concrete types in their definition. This means that if we want two or more structs to be able to satisfy a particular attribute we need to use dacquiri's multiple attribute function support.

    This works so long as we know all of the supported types ahead of time. In cases that we're building a library with dacquiri, that may not always be true. It would be better to support generic objects that satisfy some trait bounds.

    This was validated in a test that it is feasible.

    enhancement 
    opened by d0nutptr 0
  • Support for tuple contexts in attribute functions

    Support for tuple contexts in attribute functions

    Contexts, in attribute functions, don't support tuple types since we need to replace lifetimes on any borrowed types.

    We should relax this for tuple types by inspecting the elements in them and replacing the lifetimes as needed. This will allow us to handle cases like

    #[attribute]
    fn check_property(subject: &Subject, _: &(), (left, right): (Foo, &mut Bar) -> AttributeResult<Error> {
        // ...
    }
    

    which is not currently supported today

    enhancement 
    opened by d0nutptr 0
Owner
resync
resync
A simple cli tool for generating quotes in your terminal from Kanye west. Start the day out strong.

Kanyey A simple cli tool for generating quotes in your terminal from Kanye West. Install Just do cargo install kanyey and be blessed. Bonus: throw it

null 3 Sep 29, 2023
Authentication and authorization service, written in Rust

auth-rs auth-rs provides a simple authentication and authorization service for use in other services. The service is written in Rust and uses the acti

OpServa 3 Aug 17, 2023
Over-simplified, featherweight, open-source and easy-to-use authentication and authorization server.

concess ⚠️ Early Development: This is not production ready, yet. Do not use it for anything important. Introduction concess is a over-simplified, feat

Dustin Frisch 3 Nov 25, 2022
Rust library to convert RGB 24-bit colors into ANSI 256 (8-bit) color codes with zero dependencies and at compile-time.

rgb2ansi256 rgb2ansi256 is a small Rust library to convert RGB 24-bit colors into ANSI 256 (8-bit) color codes with zero dependencies and const fn. Th

Linda_pp 7 Nov 17, 2022
ChatGPT powered Rust proc macro that generates code at compile-time.

gpt-macro ChatGPT powered Rust proc macro that generates code at compile-time. Implemented Macros auto_impl!{} #[auto_test(...)] Usage Get ChatGPT API

Akira Moroo 429 Apr 15, 2023
Choose Rust types at compile-time via boolean constants

condtype Choose Rust types at compile-time via boolean constants, brought to you by Nikolai Vazquez. If you find this library useful, consider starrin

Nikolai Vazquez 36 May 8, 2023
Utilites for working with `bevy_ecs` when not all types are known at compile time

bevy_ecs_dynamic Utilities for working with bevy_ecs in situations where the types you're dealing with might not be known at compile time (e.g. script

Jakob Hellermann 17 Dec 9, 2022
tidy-builder is a builder generator that is compile-time correct.

The Builder derive macro creates a compile-time correct builder which means that it only allows you to build the given struct if and only if you provi

M.Amin Rayej 7 Dec 18, 2022
Stockbook embeds 1-bit raster images in your code at compile time

stockbook Stockbook embeds 1-bit raster images in your code at compile time. Designed primarily for #![no_std] usage, in embedded or other program-mem

Karol Belina 3 Oct 27, 2022
Catch Tailwindcss Errors at Compile-Time Before They Catch You, without making any change to your code! Supports overriding, extending, custom classes, custom modifiers, Plugins and many more 🚀🔥🦀

twust Twust is a powerful static checker in rust for TailwindCSS class names at compile-time. Table of Contents Overview Installation Usage Statement

null 15 Nov 8, 2023
CarLI is a framework for creating single-command and multi-command CLI applications in Rust

CarLI is a framework for creating single-command and multi-command CLI applications in Rust. The framework provides error and IO types better suited for the command line environment, especially in cases where unit testing is needed.

Kevin Herrera 3 Jan 21, 2022
belt is a command line app that can show your time from a list of selected time zones

A CLI app to show your time from a list of selected time zones, and a rust lib to parse dates in string formats that are commonly used.

Rollie Ma 23 Nov 4, 2022
Deadliner helps you keep track of the time left for your deadline by dynamically updating the wallpaper of your desktop with the time left.

Deadliner Watch the YouTube video What's Deadliner? Deadliner is a cross-platform desktop application for setting deadline for a project and keeping t

Deadliner 34 Dec 16, 2022
Helps you keep track of time for team members across different time zones & DST changes

Teamdate Helps you keep track of time for team members across different timezones and other daylight saving changes based off their location. Because

Alex Snaps 7 Jan 9, 2023
A universal load testing framework for Rust, with real-time tui support.

rlt A Rust Load Testing framework with real-time tui support. rlt provides a simple way to create load test tools in Rust. It is designed to be a univ

Wenxuan 129 Jul 20, 2024
compile TypeScript or JavaScript to binaries

the powr project Development is paused until 2023. ?? powr aims to be a javascript/typescript engine to power serverless functions over the web. the j

powr.js 18 Dec 29, 2022
A series of crates that I made to compile images/video into asciinema & play them.

Bad Apple A series of crates that I made to compile images/video into asciinema & play them. The end goal is to make a kernel & legacy bootloader that

S0ra 10 Nov 29, 2022
Evaluate performance gains to expect when EVM were to compile hot contracts into machine code

Convert evm bytecode to native machine code and go vroom - just an experiment, probably broken, reach out to [email protected] to contribute / productionize.

Paradigm 105 Aug 1, 2023
My own image file format created for fun! Install the "hif_opener.exe" to open hif files. clone the repo and compile to make your own hif file

Why am i creating this? I wanted to create my own image format since I was 12 years old using Windows 7, tryna modify GTA San Andreas. That day, when

hiftie 3 Dec 17, 2023