Educational Project: This is an educational implementation of an Identity and Access Management (IAM) service developed in Rust using gRPC and bitflags for high-performance permission management. It demonstrates concepts of permissions systems, bitwise operations, and gRPC service development.
bitflags-iam/
├── proto/
│ └── iam.proto # Protobuf schema defining the gRPC API
├── src/
│ ├── main.rs # gRPC IAM server
│ ├── client.rs # Example client and tests
│ ├── iam_manager.rs # Core IAM business logic and permission management
│ ├── lib.rs # Library entry point and public exports
│ ├── models.rs # Data structures and models
│ └── utils.rs # Utility functions and helpers
├── build.rs # Protobuf compilation script
├── Cargo.toml # Dependencies and project configuration
├── Cargo.lock # Dependency version lock
└── README.md # This documentation
- User creation with name, email, and password
- Secure password storage with bcrypt hashing
- In-memory storage via HashMap (no database for now)
- User information retrieval by ID
- Default permissions: READ (1), WRITE (2), EXECUTE (4), DELETE (8)
- Add/remove custom permissions
- Bitwise operations for high-performance verification
- Extensibility up to 64 simultaneous permissions
- Fast access control based on bitwise operations
- Composite verification: checks if user has ALL required permissions
- Error handling with detailed messages
- Real-time user permission updates
- Dynamic system permission management
- Thread-safe with RwLock
- Rust (version 1.70+)
- Cargo
cargo buildcargo run --bin serverThe server starts on [::1]:50051 (IPv6 localhost)
# In another terminal
cargo run --bin clientcargo build --releasecargo checkuse iam::iam_service_client::IamServiceClient;
let mut client = IamServiceClient::connect("http://[::1]:50051").await?;let request = CreateUserRequest {
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
password: "secure_password".to_string(),
permissions: vec!["READ".to_string(), "WRITE".to_string()],
};
let response = client.create_user(Request::new(request)).await?;Response:
CreateUserResponse {
success: bool,
message: String,
user: Option<User> // Contains id, name, email, permissions (Vec<String>)
}let request = GetUserRequest {
user_id: "user_uuid".to_string(),
};
let response = client.get_user(Request::new(request)).await?;Response:
GetUserResponse {
success: bool,
message: String,
user: Option<User> // User.permissions is Vec<String>
}let request = ListPermissionsRequest {};
let response = client.list_permissions(Request::new(request)).await?;Response:
ListPermissionsResponse {
success: bool,
message: String,
permissions: Vec<Permission> // Permission { name: String, value: u64 }
}let request = AddPermissionRequest {
permission_name: "ADMIN".to_string(),
};
let response = client.add_permission(Request::new(request)).await?;Response:
AddPermissionResponse {
success: bool,
message: String,
permission: Option<Permission> // Contains name and auto-assigned bit value
}let request = RemovePermissionRequest {
permission_name: "ADMIN".to_string(),
};
let response = client.remove_permission(Request::new(request)).await?;let request = UpdateUserPermissionsRequest {
user_id: "user_uuid".to_string(),
permissions: vec!["READ".to_string(), "WRITE".to_string(), "ADMIN".to_string()],
};
let response = client.update_user_permissions(Request::new(request)).await?;let request = CheckPermissionsRequest {
user_id: "user_uuid".to_string(),
required_permissions: vec!["READ".to_string(), "WRITE".to_string()],
};
let response = client.check_permissions(Request::new(request)).await?;Response:
CheckPermissionsResponse {
success: bool,
has_permissions: bool, // true if user has ALL required permissions
message: String,
missing_permissions: Vec<String> // List of missing permissions if any
}The service uses 64-bit bitflags internally for high-performance permission checking, but clients work with human-readable permission names.
| Permission | Value | Binary |
|---|---|---|
| READ | 1 | 0001 |
| WRITE | 2 | 0010 |
| EXECUTE | 4 | 0100 |
| DELETE | 8 | 1000 |
// Create user with named permissions
let permissions = vec!["READ".to_string(), "WRITE".to_string()];
// Check multiple permissions at once
let required = vec!["READ".to_string(), "ADMIN".to_string()];
// Add new permission (automatically assigns bit value)
let request = AddPermissionRequest {
permission_name: "ADMIN".to_string(),
};The service automatically handles the conversion between permission names and bit values:
- Permission names → bit values for internal storage
- Bit values → permission names for client responses
- Bitwise AND operations for permission checking
// The service verifies that users have ALL required permissions
// Internally: (user_permissions & required_permissions) == required_permissionsWhen adding new permissions, the service automatically finds the next available bit position:
- First available: 16 (1 << 4)
- Next: 32 (1 << 5)
- Up to: 1 << 63 (64th bit)
"User not found"- User ID doesn't exist"Permission already exists"- Attempt to add existing permission"Permission not found"- Attempt to remove non-existent permission"Failed to hash password"- Error during password hashing"Failed to acquire write lock"- Concurrency error (rare)
- Edit
proto/iam.proto - Rebuild:
cargo build - Rust bindings are generated automatically
- gRPC handlers are in
src/main.rs - Business logic is in
IamManager - Thread-safe by design with
Arc<RwLock<>>
- In-memory storage: O(1) access for users
- Bitwise operations: O(1) permission checking
- Thread-safe: Concurrency support with RwLock
See src/client.rs for a complete usage example demonstrating:
- Permission listing: View all available permissions
- User creation: Create users with named permissions
- Permission checking: Check multiple permissions with detailed feedback
- Dynamic permissions: Add new permissions at runtime
- Permission updates: Modify user permissions
- Error handling: Handle non-existent permissions gracefully
- Human-readable: Use
"ADMIN"instead of16 - Multiple permissions: Check
["READ", "WRITE", "ADMIN"]at once - Auto-assignment: Service assigns bit values automatically
- Missing permissions: Get detailed list of what's missing
- Discovery: List all available permissions
# Start server
cargo run --bin server
# Run example client (in another terminal)
cargo run --bin clientThe client will demonstrate all features including creating a user with ["READ", "WRITE"] permissions, adding an "ADMIN" permission, checking various permission combinations, and showing detailed error messages.