Skip to content

Commit e27024d

Browse files
committed
Add IAM permissions and roles documentation
Document permission structure (service.resource.action), wildcard usage, and deny-by-default security model. Explain role and permission bindings, project-scoped access, and token introspection flow. Include API examples for creating permissions, roles, and bindings. Add service integration guide with Python code examples for permission checks and middleware. Cover best practices, error handling, and important notes on caching and access control.
1 parent 5e91770 commit e27024d

1 file changed

Lines changed: 377 additions & 0 deletions

File tree

docs/iam/permissions_overview.md

Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
## 1. Permissions Overview
2+
3+
In the Identity and Access Management (IAM) system, a **Permission** is a core entity that defines the right to perform a specific action on a specific resource. IAM operates on a **"deny by default" model**: if a user or service does not have an explicitly granted permission for an action, that action is blocked.
4+
5+
### 1.1. Permission Structure
6+
7+
Each permission is represented as a string consisting of three parts separated by dots:
8+
9+
`<service_name>.<resource_name>.<action>`
10+
11+
* **`<service_name>`**:
12+
* A unique identifier for a service or module in your system
13+
* Examples: `billing`, `compute`, `auth`, `storage`
14+
15+
* **`<resource_name>`**:
16+
* Defines the type of object an action can be performed on
17+
* Expressed in the **singular form**
18+
* Examples: `account`, `vm`, `user`, `policy`
19+
20+
* **`<action>`**:
21+
* Defines the operation that can be performed on the specified resource
22+
* Examples: `read`, `write`, `create`, `delete`, `activate`
23+
24+
**Permission examples:**
25+
26+
* `billing.account.read` — view account information in the billing service
27+
* `compute.vm.create` — create a new virtual machine in the compute service
28+
* `auth.user.deactivate` — deactivate a user in the authentication service
29+
30+
You can also use `*` (wildcard) in any of the three parts. It means **all** values are allowed in that part.
31+
32+
**How `*` works by part:**
33+
34+
* `*` in the first part (`service_name`) — access for **all services**
35+
* `*` in the second part (`resource_name`) — access for **all resources** in the selected service
36+
* `*` in the third part (`action`) — access for **all actions** on the selected resource
37+
38+
**Wildcard permission examples:**
39+
40+
* `*.vm.read` — allows reading `vm` in all services
41+
* `compute.*.read` — allows reading any resource in the `compute` service
42+
* `compute.vm.*` — allows any action on `vm` in the `compute` service
43+
* `*.*.*` — full access to all services, resources, and actions
44+
45+
### 1.2. Security Model: "Deny by Default"
46+
47+
An important IAM philosophy is the principle of **explicit permission**:
48+
49+
* Initially, any user or service account has no access rights
50+
* To perform any action, the subject must be granted the corresponding permission
51+
* Services integrated with IAM must check for the required permission before executing an operation
52+
53+
### 1.3. Permissions API Examples
54+
55+
#### Create a Permission
56+
57+
```http
58+
POST /v1/iam/permissions/
59+
Content-Type: application/json
60+
Authorization: Bearer <token>
61+
62+
{
63+
"name": "compute.vm.create",
64+
"description": "Permission to create a virtual machine"
65+
}
66+
```
67+
68+
#### Response
69+
70+
```json
71+
{
72+
"uuid": "5a1b2c3d-4e5f-6789-abcd-ef0123456789",
73+
"name": "compute.vm.create",
74+
"description": "Permission to create a virtual machine",
75+
"created_at": "2025-08-21T07:38:04.778680Z",
76+
"updated_at": "2025-08-21T07:38:04.778688Z",
77+
"status": "ACTIVE"
78+
}
79+
```
80+
81+
#### Get a Permission
82+
83+
```http
84+
GET /v1/iam/permissions/5a1b2c3d-4e5f-6789-abcd-ef0123456789
85+
Authorization: Bearer <token>
86+
```
87+
88+
#### Filter Permissions
89+
90+
```http
91+
GET /v1/iam/permissions/?name=compute.vm.create&status=ACTIVE
92+
Authorization: Bearer <token>
93+
```
94+
95+
## 2. Roles
96+
97+
A **Role** is a named collection of Permissions. While a Permission defines the right for one specific action, a Role groups these rights into logical blocks corresponding to job functions, responsibilities, or the access level of a user or service.
98+
99+
### 2.1. Permission Binding: Linking Roles and Permissions
100+
101+
To assign permissions to a role, the **Permission Binding** entity is used. This entity establishes a many-to-many relationship between roles and permissions.
102+
103+
* **One Permission** can be bound to **several different roles**
104+
* **One Role** can contain **many different permissions**
105+
106+
**Example:**
107+
108+
* The `BillingViewer` role receives the following via Permission Binding:
109+
* `billing.account.read`
110+
* `billing.invoice.read`
111+
* The `BillingOperator` role receives the following via Permission Binding:
112+
* `billing.account.read`
113+
* `billing.invoice.read`
114+
* `billing.invoice.pay`
115+
116+
### 2.2. Role Binding: Assigning Roles to Users
117+
118+
To grant a user access, a role must be assigned to them. The **Role Binding** entity is used for this purpose. This entity establishes a many-to-many relationship between users and roles.
119+
120+
* **One user** can be assigned **several roles**
121+
* **One role** can be assigned to **many users**
122+
123+
### 2.3. Final Access Model
124+
125+
The access verification process:
126+
127+
1. A **User** calls a service with a token
128+
2. The **Service** calls IAM token introspection
129+
3. The service gets the list of permissions available for this token from the introspection response
130+
4. The **Service** checks whether the required permission exists for the requested action
131+
5. If the permission is present, access is **granted**
132+
133+
### 2.4. Roles API Examples
134+
135+
#### Create a Role
136+
137+
```http
138+
POST /v1/iam/roles/
139+
Content-Type: application/json
140+
Authorization: Bearer <token>
141+
142+
{
143+
"name": "BillingOperator",
144+
"description": "Billing operator with rights to manage accounts"
145+
}
146+
```
147+
148+
#### Response
149+
150+
```json
151+
{
152+
"uuid": "6b2c3d4e-5f67-789a-bcde-f01234567890",
153+
"name": "BillingOperator",
154+
"description": "Billing operator with rights to manage accounts",
155+
"created_at": "2025-08-21T07:38:04.779416Z",
156+
"updated_at": "2025-08-21T07:38:04.779424Z",
157+
"status": "ACTIVE",
158+
"project_id": null
159+
}
160+
```
161+
162+
#### Create a Permission Binding
163+
164+
```http
165+
POST /v1/iam/permission_bindings/
166+
Content-Type: application/json
167+
Authorization: Bearer <token>
168+
169+
{
170+
"role": "6b2c3d4e-5f67-789a-bcde-f01234567890",
171+
"permission": "5a1b2c3d-4e5f-6789-abcd-ef0123456789"
172+
}
173+
```
174+
175+
#### Create a Role Binding
176+
177+
```http
178+
POST /v1/iam/role_bindings/
179+
Content-Type: application/json
180+
Authorization: Bearer <token>
181+
182+
{
183+
"user": "7c3d4e5f-6789-89ab-cdef-123456789012",
184+
"role": "6b2c3d4e-5f67-789a-bcde-f01234567890",
185+
"project": "8d4e5f67-789a-9abc-def1-234567890123"
186+
}
187+
```
188+
189+
#### Get User Roles
190+
191+
```http
192+
GET /v1/iam/users/7c3d4e5f-6789-89ab-cdef-123456789012/actions/get_my_roles
193+
Authorization: Bearer <token>
194+
```
195+
196+
### 2.5. Projects and Role/Permission Scope
197+
198+
In IAM, roles can be assigned as:
199+
200+
* **Global** (without project, `project = null`)
201+
* **Project-scoped** (via the `project` field in `Role Binding`)
202+
203+
This directly affects the effective permission set in a token.
204+
205+
When issuing a token, IAM determines the project from `scope`:
206+
207+
* `project:<uuid>` — token is bound to the specified project
208+
* `project:default` — token is bound to the user's default project
209+
* if no `project:...` segment is present in `scope`, the token is issued **without project** (`project = null`)
210+
211+
During introspection, IAM returns permissions only in the context of the token's project.
212+
In other words, the effective permission set depends on which project the token is issued for.
213+
214+
Important:
215+
getting a token **without project** does not automatically grant access to all projects.
216+
Such token includes only permissions valid in `project = null` context (global assignments) and does not include project-scoped assignments from other projects.
217+
218+
### 2.6. How Services Should Check Permissions (Practical Template)
219+
220+
Below is a recommended integration model for a business service with IAM.
221+
222+
#### Basic flow
223+
224+
1. Service receives a user token (`Authorization: Bearer ...`)
225+
2. Service calls IAM token introspection endpoint
226+
3. Service gets the permission list from introspection for the current token
227+
4. Before each protected action, service checks that required permission is present
228+
5. If permission is missing, service returns `403 Forbidden`
229+
230+
#### Minimal service code structure
231+
232+
```python
233+
class IamClient:
234+
def introspect(self, token: str) -> dict:
235+
# HTTP GET /v1/iam/clients/<client_uuid>/actions/introspect/invoke
236+
# with Authorization: Bearer <token>
237+
...
238+
239+
240+
class AuthContext:
241+
def __init__(self, token: str, introspection: dict):
242+
self.token = token
243+
self.introspection = introspection
244+
self.permissions = {
245+
p["name"] if isinstance(p, dict) else str(p)
246+
for p in introspection.get("permissions", [])
247+
}
248+
249+
def has_permission(self, permission_name: str) -> bool:
250+
return permission_name in self.permissions
251+
252+
253+
class PermissionDenied(Exception):
254+
pass
255+
```
256+
257+
#### Guard/decorator for permission checks
258+
259+
```python
260+
def require_permission(permission_name: str):
261+
def wrapper(handler):
262+
def inner(request, *args, **kwargs):
263+
ctx: AuthContext = request.auth_context
264+
if not ctx.has_permission(permission_name):
265+
raise PermissionDenied(
266+
f"Missing required permission: {permission_name}"
267+
)
268+
return handler(request, *args, **kwargs)
269+
return inner
270+
return wrapper
271+
```
272+
273+
#### Endpoint example
274+
275+
```python
276+
@require_permission("compute.vm.create")
277+
def create_vm(request):
278+
payload = request.json
279+
# VM creation business logic
280+
return {"status": "ok"}, 201
281+
```
282+
283+
#### Context initialization in middleware
284+
285+
```python
286+
def auth_middleware(request, iam_client: IamClient):
287+
token = extract_bearer_token(request.headers)
288+
if not token:
289+
return {"error": "Unauthorized"}, 401
290+
291+
introspection = iam_client.introspect(token)
292+
request.auth_context = AuthContext(token=token, introspection=introspection)
293+
return None # continue
294+
```
295+
296+
#### Practical recommendations
297+
298+
* Check permissions as close as possible to action execution point (endpoint/use-case)
299+
* Do not infer rights in the service — IAM must stay the source of truth
300+
* Only short-lived server-side cache for introspection response is acceptable; do not cache permissions on the client side
301+
* Log access denials with required permission and user/token context
302+
303+
## 3. Best Practices
304+
305+
### 3.1. Principle of Least Privilege
306+
307+
Create roles that provide exactly the level of access required to perform a task, and nothing more.
308+
309+
### 3.2. Semantic Naming
310+
311+
Give roles and permissions clear names that reflect their purpose:
312+
313+
* Roles: `NetworkReadOnly`, `DatabaseSuperUser`
314+
* Permissions: `compute.vm.read`, `storage.bucket.delete`
315+
316+
### 3.3. Regular Audits
317+
318+
Periodically review:
319+
320+
* Which roles are assigned to whom
321+
* Which permissions are included in roles
322+
* Remove unnecessary access promptly
323+
324+
### 3.4. Using Projects for Isolation
325+
326+
Use project-scoped Role Binding to isolate access between environments.
327+
Detailed project-context behavior is described in section **2.5**.
328+
329+
## 4. Error Handling
330+
331+
The following errors may occur when working with the IAM API:
332+
333+
### 4.1. Access Error (403 Forbidden)
334+
335+
```json
336+
{
337+
"status": 403,
338+
"json": {
339+
"code": 403,
340+
"type": "PermissionDeniedException",
341+
"message": "User does not have required permission: compute.vm.create"
342+
}
343+
}
344+
```
345+
346+
### 4.2. Not Found (404 Not Found)
347+
348+
```json
349+
{
350+
"status": 404,
351+
"json": {
352+
"code": 404,
353+
"type": "NotFoundException",
354+
"message": "Role with uuid 6b2c3d4e-5f67-789a-bcde-f01234567890 not found"
355+
}
356+
}
357+
```
358+
359+
### 4.3. Bad Request (400 Bad Request)
360+
361+
```json
362+
{
363+
"status": 400,
364+
"json": {
365+
"code": 400,
366+
"type": "ValidationErrorException",
367+
"message": "Field 'name' must be between 0 and 255 characters"
368+
}
369+
}
370+
```
371+
372+
## 5. Important Notes
373+
374+
1. All changes to permissions and role bindings take effect immediately
375+
2. Do not cache permissions on the client side; if caching is needed, use only short-lived server-side introspection cache
376+
3. For service accounts, use separate roles with the minimum required permissions
377+
4. Regularly update and review role assignments within the system

0 commit comments

Comments
 (0)