1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Multi-tenant [asynq](https://github.com/emo-crab/asynq/) example using Redis ACLs
//!
//! Purpose
//! - Demonstrate a pattern for multi-tenant queues using key prefixes and Redis ACLs.
//! - Provide a public/shared default queue that everyone can access and tenant-specific
//! queues that are isolated by prefixing keys with the tenant id.
//! - Show how an admin creates a tenant user with restricted permissions and how a tenant
//! uses that account to operate on allowed keys only.
//!
//! Design summary (short):
//! - Default (public) queue: asynq:{default}:* (accessible by all tenants)
//! - Tenant-specific queues: asynq:{<tenant>:<queue_name>}:* (only accessible by that tenant)
//! - Admin creates users and assigns ACL rules that: enable the user, set a password,
//! remove dangerous commands, allow only specific key patterns and channels.
//!
//! Contract (what this example shows):
//! - Inputs: an admin Redis connection (to run ACL SETUSER) and a tenant username/password.
//! - Outputs: a Redis user configured with ACL rules that limit commands and key patterns.
//! - Error modes: connection errors, ACL command failures.
//!
//! Quick ASCII flowchart (high level):
//!```
//! Admin (operator)
//! │
//! │ 1. CONNECT as admin
//! │ 2. ACL SETUSER tenant1 on >password +@all -@dangerous ~asynq:{default}:* ~asynq:{tenant1:* ...
//! ▼
//! Redis Server (stores ACLs + data)
//! │
//! ├─ stores ACL rules tied to 'tenant1' user
//! ├─ keeps public queue keys: asynq:{default}:pending, asynq:{default}:processing, ...
//! └─ keeps tenant queues: asynq:{tenant1:critical}:pending, asynq:{tenant1:low}:pending ...
//!
//! Tenant (tenant1)
//! │
//! │ 3. CONNECT using tenant1 / password
//! │ 4. PUSH tasks to queues within allowed key patterns
//! │ 5. POP / PROCESS tasks only from allowed keys (or default public queue)
//! ▼
//! Behavior notes:
//! - Tenant cannot run commands in the removed categories (e.g. -@dangerous)
//! - Tenant cannot access keys outside the allowed patterns
//! - Default queue is intentionally left accessible to allow cross-tenant shared tasks
//!```
//! Example usage flow (concrete):
//! 1) Admin: acl setuser tenant1 on >securepassword +@all -@dangerous ~asynq:{default}:* ~asynq:{tenant1:* ...
//! 2) Tenant: AUTH tenant1 securepassword
//! 3) Tenant: PUSH asynq:{tenant1:critical}:pending <task>
//! 4) Worker (authed as tenant1): POP asynq:{tenant1:critical}:pending
//! 5) All tenants can POP asynq:{default}:pending
//!
//! Why the curly-brace/hashtag? For Redis Cluster, using {...} creates a hashtag so all
//! keys that share the same tag are guaranteed to be in the same hash slot. This makes
//! multi-key atomic operations that rely on colocated keys work as expected.
//!
use Rule;
use ;
const DEFAULT_QUEUE_NAME: &str = "default";
/// With ACL enabled (tenant: tenant1):
///
/// default queue → asynq:{default}:pending (shared, no prefix)
/// critical queue → asynq:{tenant1:critical}:pending (tenant-specific, prefixed)
/// low queue → asynq:{tenant1:low}:pending (tenant-specific, prefixed)
///
/// This design allows the default queue to serve as a shared public queue accessible by all tenants,
/// while still providing tenant isolation for custom queues. Tenants needing fully isolated queues
/// should use custom queue names which will be properly prefixed.