Identity & Access Management

Preview — pending review

This reference is generated from the platform API contract and is still under review. Operations, fields, and descriptions can change before publication.

Grant the member one access row

POST
/tenants/{tenantId}/iam/users/{userId}/access

Adds ONE row. A no-op when the member already holds it or a row of the same role covers it (reported in dropped). The member's editable rows of the same role the new row covers are removed and returned in removed (an upgrade to a subscription or to the whole tenant). Single-row errors carry index: 0 and a field without the access.0. prefix. Rows are checked at their OWN target exactly as on the invite (POST /tenants/{tenantId}/iam/users): 404 SCOPE_NOT_FOUND; 403 INSUFFICIENT_ROLE_FOR_SCOPE / CANNOT_GRANT_ABOVE_OWN / TENANT_OWNER_REQUIRED; 422 ROLE_NOT_FOUND / ROLE_NOT_ASSIGNABLE_AT_SCOPE; precedence 404 > 403 > 422 with details.errors[{index, field, code, message}]. 409 details.reason: LAST_TENANT_OWNER (the change would leave the tenant without an owner) or SELF_IAM_MANAGE_REMOVAL (the caller would remove one of its own rows that confers iam: manage). Every added / removed row is one iam.user.access.grant / iam.user.access.revoke Activity Log record and re-projects the member's OpenStack roles. The answer is the full matrix after the change.

Step-up: granting an owner-ranked role (built-in owner, or a custom role that ranks as owner) from a console session whose account has two-factor authentication needs the factor verified in the last 5 minutes, otherwise 403 MFA_STEP_UP_REQUIRED (verify with POST /auth/step-up, then retry). Other roles, and an owner row the member already holds, are not affected.

Authorization

bearerToken
headerAuthorizationBearer <token>

An API token (pc_live_…) or a service-account key (sak_live_…), created in Identity & Access Management and sent as Authorization: Bearer <token>. Requests are rate limited per credential and per tenant: every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and a refused request gets 429 RATE_LIMITED with Retry-After.

Path Parameters

tenantId*string

The tenant id.

userId*string

The member (its id is its email).

Request Body

application/json
  1. body

One row of a member's access matrix -- a role at one scope of the tenant.

scopeKind*RoleAssignmentScopeKind

Scope kind targeted by a role assignment. A tenant assignment (scope id = the tenant id) covers every subscription of the tenant and every project beneath them, including ones created later.

Value in"tenant""subscription""project"
scopeId*string

The tenant id (tenant), a subscription id (subscription) or a project id (project).

Length1 <= length
role*string

A built-in role id or alias (owner, contributor, reader, billing-ops) or a custom role id (rol_…) of the tenant.

Length1 <= length

Response Body

The matrix after the change.

application/json
  1. response

A member's access matrix in the tenant.

userId*string
tenantId*string
rows*array<>
hiddenCount*integer

Rows at scopes the caller does not reach (not listed, never changed by its writes).

Range0 <= value
summary*

What a member can reach in the tenant, for list rows. tenant when it holds any tenant row; scoped when it holds subscription/project rows only; none when it holds no row. The counts are the distinct subscriptions / projects named by its explicit rows (computed over every row, including ones the viewer cannot edit).

dropped*array<>

Input rows not written because another row already grants them (writes only).

removed*array<>

Rows the write removed (writes only).

curl -X POST "https://example.com/tenants/string/iam/users/string/access" \  -H "Content-Type: application/json" \  -d '{    "scopeKind": "tenant",    "scopeId": "string",    "role": "string"  }'
{  "userId": "string",  "tenantId": "string",  "rows": [    {      "assignmentId": "string",      "scopeKind": "tenant",      "scopeId": "string",      "scopeName": "string",      "subscriptionId": "string",      "role": "string",      "roleName": "string",      "grantedBy": "string",      "createdAt": "2019-08-24T14:15:22Z",      "editable": true    }  ],  "hiddenCount": 0,  "summary": {    "kind": "tenant",    "subscriptions": 0,    "projects": 0  },  "dropped": [    {      "index": 0,      "scopeKind": "tenant",      "scopeId": "string",      "role": "string",      "reason": "DUPLICATE",      "coveredBy": {        "scopeKind": "tenant",        "scopeId": "string"      }    }  ],  "removed": [    {      "assignmentId": "string",      "scopeKind": "tenant",      "scopeId": "string",      "scopeName": "string",      "subscriptionId": "string",      "role": "string",      "roleName": "string",      "grantedBy": "string",      "createdAt": "2019-08-24T14:15:22Z",      "editable": true    }  ]}