OpenStack Identity API v3

鉴于苦苦寻找V3 API,贴与此,一起学习。



The Identity API primarily fulfills authentication and authorization needs within OpenStack, and is intended to provide a programmatic facade in front of existing authentication and authorization system(s).

The Identity API also provides endpoint discovery through a service catalog, identity management, project management, and a centralized repository for policy engine rule sets.

What's New in Version 3.2

  • Extension Discovery.

What's New in Version 3.1

  • A token without an explicit scope of authorization is issued if the user does not specify a project and does not have authorization on the project specified by their default project attribute.
  • Introduced a generalized call for getting role assignments, with filtering for user, group, project, domain and role.
  • Introduced a mechanism to opt-out from catalog information during token creation
  • Added optional bind information to token structure.

What's New in Version 3.0

  • Former "Service" and "Admin" APIs (including CRUD operations previously defined in the v2 OS-KSADM extension) are consolidated into a single core API
  • "Tenants" are now known as "projects"
  • "Groups": a container representing a collection of users
  • "Domains": a high-level container for projects, users and groups
  • "Policies": a centralized repository for policy engine rule sets
  • "Credentials": generic credential storage per user (e.g. EC2, PKI, SSH, etc.)
  • Roles can be granted at either the domain or project level
  • User, group and project names only have to be unique within their owning domain
  • Retrieving your list of projects (previously GET /tenants) is now explicitly based on your user ID:GET /users/{user_id}/projects
  • Tokens explicitly represent user+project or user+domain pairs
  • Partial updates are performed using the HTTP PATCH method
  • Token ID values no longer appear in URLs

API Conventions

The Identity API provides a RESTful JSON interface.

Each REST resource contains a canonically unique identifier (ID) defined by the Identity service implementation and is provided as the id attribute; Resource ID's are strings of non-zero length.

The resource paths of all collections are plural and are represented at the root of the API (e.g. /v3/policies).

TCP port 35357 is designated by the Internet Assigned Numbers Authority ("IANA") for use by OpenStack Identity services. Example API requests & responses in this document therefore assume that the Identity service implementation is deployed at the root ofhttp://identity:35357/.

Required Attributes

Headers:

  • X-Auth-Token

    This header is used to convey the authentication token when accessing Identity APIs.

  • X-Subject-Token

    This header is used to convey the subject of the request for token-related operations.

For collections:

  • links (object)

    Specifies a list of relational links to the collection.

    • self (url)

    A self-relational link provided as an absolute URL. This attribute is provided by the identity service implementation.

    • previous (url)

    A relational link to the previous page of the list, provided as an absolute URL. This attribute is provided by the identity service implementation. May be null.

    • next (url)

    A relational to the next page of the list, provided as an absolute URL. This attribute is provided by the identity service implementation. May be null.

For members:

  • id (string)

    Globally unique resource identifier. This attribute is provided by the identity service implementation.

  • links (object)

    Specifies a set of relational links relative to the collection member.

    • self (url)

    A self-relational link provided as an absolute URL. This attribute is provided by the identity service implementation.

CRUD Operations

Unless otherwise documented (tokens being the notable exception), all resources provided by the Identity API support basic CRUD operations (create, read, update, delete).

The examples in this section utilize a resource collection of Entities on /v3/entities which is not actually a part of the Identity API, and is used for illustrative purposes only.

Create an Entity

When creating an entity, you must provide all required attributes (except those provided by the Identity service implementation, such as the resource ID):

Request:

POST /entities

{
    "entity": {
        "name": string,
        "description": string,
        "enabled": boolean
    }
}

The full entity is returned in a successful response (including the new resource's ID and a self-relational link), keyed by the singular form of the resource name:

201 Created

{
    "entity": {
        "id": string,
        "name": string,
        "description": string,
        "enabled": boolean,
        "links": {
            "self": url
        }
    }
}
List Entities

Request the entire collection of entities:

GET /entities

A successful response includes a list of anonymous dictionaries, keyed by the plural form of the resource name (identical to that found in the resource URL):

200 OK

{
    "entities": [
        {
            "id": string,
            "name": string,
            "description": string,
            "enabled": boolean,
            "links": {
                "self": url
            }
        },
        {
            "id": string,
            "name": string,
            "description": string,
            "enabled": boolean,
            "links": {
                "self": url
            }
        }
    ],
    "links": {
        "self": url,
        "next": url,
        "previous": url
    }
}
List Entities filtered by attribute

Beyond each resource's canonically unique identifier (the id attribute), not all attributes are guaranteed unique on their own. To list resources which match a specified attribute value, we can perform a filter query using a query string with one or more attribute/value pairs:

GET /entities?name={entity_name}&enabled={entity_enabled}

The response is a subset of the full collection:

200 OK

{
    "entities": [
        {
            "id": string,
            "name": string,
            "description": string,
            "enabled": boolean,
            "links": {
                "self": url
            }
        }
    ],
    "links": {
        "self": url,
        "next": url,
        "previous": url
    }
}
Get an Entity

Request a specific entity by ID:

GET /entities/{entity_id}

The full resource is returned in response:

200 OK

{
    "entity": {
        "id": string,
        "name": string,
        "description": string,
        "enabled": boolean,
        "links": {
            "self": url
        }
    }
}
Nested collections

An entity may contain nested collections, in which case the required attributes for collections still apply; however, to avoid conflicts with other required attributes, the required attributes of the collection are prefixed with the name of the collection. For example, if anentity contains a nested collection of objects, the links for the collection of objects is called objects_links:

{
    "entity": {
        "id": string,
        "name": string,
        "description": string,
        "enabled": boolean,
        "links": {
            "self": url
        },
        "objects": [
            {
                "id": string,
                "name": string,
                "description": string,
                "enabled": boolean,
                "links": {
                    "self": url
                }
            }
        ],
        "objects_links": {
            "self": url,
            "next": url,
            "previous": url
        }
    }
}
Update an Entity

Partially update an entity (unlike a standard PUT operation, only the specified attributes are replaced):

PATCH /entities/{entity_id}

{
    "entity": {
        "description": string
    }
}

The full entity is returned in response:

200 OK

{
    "entity": {
        "id": string,
        "name": string,
        "description": string,
        "enabled": boolean,
        "links": {
            "self": url
        }
    }
}
Delete an Entity

Delete a specific entity by ID:

DELETE /entities/{entity_id}

A successful response does not include a body:

204 No Content

HTTP Status Codes

The Identity API uses a subset of the available HTTP status codes to communicate specific success and failure conditions to the client.

200 OK

This status code is returned in response to successful GET and PATCH operations.

201 Created

This status code is returned in response to successful POST operations.

204 No Content

This status code is returned in response to successful HEADPUT and DELETE operations.

300 Multiple Choices

This status code is returned by the root identity endpoint, with references to one or more Identity API versions (such as /v3/).

400 Bad Request

This status code is returned when the Identity service fails to parse the request as expected. This is most frequently returned when a required attribute is missing, a disallowed attribute is specified (such as an id on POST in a basic CRUD operation), or an attribute is provided of an unexpected data type.

The client is assumed to be in error.

401 Unauthorized

This status code is returned when either authentication has not been performed, the provided X-Auth-Token is invalid or authentication credentials are invalid (including the user, project or domain having been disabled).

403 Forbidden

This status code is returned when the request is successfully authenticated but not authorized to perform the requested action.

404 Not Found

This status code is returned in response to failed GETHEADPOSTPUTPATCH and DELETE operations when a referenced entity cannot be found by ID. In the case of a POST request, the referenced entity may be in the request body as opposed to the resource path.

409 Conflict

This status code is returned in response to failed POST and PATCH operations. For example, when a client attempts to update an entity's unique attribute which conflicts with that of another entity in the same collection.

Alternatively, a client should expect this status code when attempting to perform the same create operation twice in a row on a collection with a user-defined and unique attribute. For example, a User's name attribute is defined to be unique and user-defined, so making the same POST /users request twice in a row will result in this status code.

The client is assumed to be in error.

500 Internal Server Error

This status code is returned when an unexpected error has occurred in the Identity service implementation.

501 Not Implemented

This status code is returned when the Identity service implementation is unable to fulfill the request because it is incapable of implementing the entire API as specified.

For example, an Identity service may be incapable of returning an exhaustive collection of Projects with any reasonable expectation of performance, or lack the necessary permission to create or modify the collection of users (which may be managed by a remote system); the implementation may therefore choose to return this status code to communicate this condition to the client.

503 Service Unavailable

This status code is returned when the Identity service is unable to communicate with a backend service, or by a proxy in front of the Identity service unable to communicate with the Identity service itself.

API Resources

Users: /v3/users

User entities represent individual API consumers and are owned by a specific domain.

Role grants explicitly associate users with projects or domains. Each user-project or user-domain pair can have a unique set of roles granted on them.

A user without any role grants is effectively useless from the perspective of an OpenStack service and should never have access to any resources. It is allowed, however, as a means of acquiring or loading users from external sources prior to mapping them to projects.

Additional required attributes:

  • name (string)

    Either globally or domain unique username, depending on owning domain.

Optional attributes:

  • domain_id (string)

    References the domain which owns the user; if a domain is not specified by the client, the Identity service implementation will default it to the domain to which the client's token is scoped.

  • default_project_id (string)

    References the user's default project against which to authorize, if the API user does not explicitly specify one when creating a token. Setting this attribute does not grant any actual authorization on the project, and is merely provided for the user's convenience. Therefore, the referenced project does not need to exist within the user's domain.

    New in version 3.1 If the user does not have authorization to their default project, the default project will be ignored at token creation.

  • description (string)

  • enabled (boolean)

    Setting this value to false prevents the user from authenticating or receiving authorization. Additionally, all pre-existing tokens held by the user are immediately invalidated. Re-enabling a user does not re-enable pre-existing tokens.

  • password (string)

    The default form of credential used during authentication.

Example entity:

{
    "user": {
        "default_project_id": "263fd9",
        "domain_id": "1789d1",
        "email": "joe@example.com",
        "enabled": true,
        "id": "0ca8f6",
        "links": {
            "self": "http://identity:35357/v3/users/0ca8f6"
        },
        "name": "Joe"
    }
}

Groups: /v3/groups

Group entities represent a collection of Users and are owned by a specific domain. As with individual users, role grants explicitly associate groups with projects or domains. A group role grant onto a project/domain is the equivalent of granting each individual member of the group the role on that project/domain. Once a group role grant has been made, the addition or removal of a user to such a group will result in the automatic granting/revoking of that role to the user, which will also cause any token containing that user and project/domain to be revoked.

As with users, a group entity without any role grants is effectively useless from the perspective an OpenStack service and should never have access to any resources. It is allowed, however, as a means of acquiring or loading users/groups from external sources prior to mapping them to projects/domains.

Additional required attributes:

  • name (string)

    Unique group name, within the owning domain.

Optional attributes:

  • domain_id (string)

    References the domain which owns the group; if a domain is not specified by the client, the Identity service implementation will default it to the domain to which the client's token is scoped.

  • description (string)

Example entity:

{
    "group": {
        "description": "Developers cleared for work on all general projects"
        "domain_id": "1789d1",
        "id": "70febc",
        "links": {
            "self": "http://identity:35357/v3/groups/70febc"
        },
        "name": "Developers"
    }
}

Credentials: /v3/credentials

Credentials represent arbitrary authentication credentials associated with a user. A user may have zero or more credentials, each optionally scoped to a specific project.

Additional required attributes:

  • user_id (string)

    References the user which owns the credential.

  • type (string)

    Representing the credential type, such as ec2 or cert. A specific implementation may determine the list of supported types.

  • blob (blob)

    Arbitrary blob of the credential data, to be parsed according to the type.

Optional attributes:

  • project_id (string)

    References a project which limits the scope the credential applies to.

Example entity:

{
    "credential": {
        "blob": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
        "id": "80239a",
        "links": {
            "self": "http://identity:35357/v3/credentials/80239a"
        },
        "project_id": "263fd9",
        "type": "ec2",
        "user_id": "0ca8f6"
    }
}

Projects: /v3/projects

Projects represent the base unit of "ownership" in OpenStack, in that all resources in OpenStack should be owned by a specific project ("projects" were also formerly known as "tenants"). A project itself must be owned by a specific domain.

Required attributes:

  • name (string)

    Either globally or domain unique project name, depending on owning domain.

Optional attributes:

  • domain_id (string)

    References the domain which owns the project; if a domain is not specified by the client, the Identity service implementation will default it to the domain to which the client's token is scoped.

  • description (string)

  • enabled (boolean)

    Setting this attribute to false prevents users from authorizing against this project. Additionally, all pre-existing tokens authorized for the project are immediately invalidated. Re-enabling a project does not re-enable pre-existing tokens.

Example entity:

{
    "project": {
        "domain_id": "1789d1",
        "enabled": true,
        "id": "263fd9",
        "name": "project-x",
        "links": {
            "self": "http://identity:35357/v3/projects/263fd9"
        }
    }
}

Domains: /v3/domains

Domains represent collections of users, groups and projects. Each is owned by exactly one domain. Users, however, can be associated with multiple projects by granting roles to the user on a project (including projects owned by other domains).

Each domain defines a namespace in which certain API-visible name attributes exist, which affects whether those names need to be globally unique or simply unique within that domain. Within the Identity API, there are five such name attributes, whose uniqueness are affected by the domain:

  • Domain Name: This is always globally unique across all domains.

  • Role Name: This is always globally unique across all domains.

  • User Name: This is only unique within the owning domain.

  • Project Name: This is only unique within the owning domain.

  • Group Name: This is only unique within the owning domain.

Additional required attributes:

  • name (string)

    Globally unique name.

Optional attributes:

  • description (string)

  • enabled (boolean)

    Setting this attribute to false prevents users from authorizing against this domain or any projects owned by this domain, and prevents users owned by this domain from authenticating or receiving any other authorization. Additionally, all pre-existing tokens applicable to the above entities are immediately invalidated. Re-enabling a domain does not re-enable pre-existing tokens.

Example entity:

{
    "domain": {
        "enabled": true,
        "id": "1789d1",
        "links": {
            "self": "http://identity:35357/v3/domains/1789d1"
        },
        "name": "example.com"
    }
}

Roles: /v3/roles/

Roles entities are named identifiers used to map a collection of actions from a user to either a specific project or across an entire domain.

Additional required attributes:

  • name (string)

    Globally unique name of the role.

Example entity:

{
    "role": {
        "id": "76e72a",
        "links": {
            "self": "http://identity:35357/v3/roles/76e72a"
        },
        "name": "admin"
    }
}

Services: /v3/services

Service entities represent web services in the OpenStack deployment. A service may have zero or more endpoints associated with it, although a service with zero endpoints is essentially useless in an OpenStack configuration.

Additional required attributes:

  • type (string)

    Describes the API implemented by the service. The following values are recognized within the OpenStack ecosystem:computeimageec2identityvolumenetwork. To support non-core and future projects, the value should not be validated against this list.

Optional attributes:

  • name (string)

    User-facing name of the service.

  • enabled (boolean)

    Setting this value to false prevents the service and its endpoints from appearing in the service catalog.

Example entity:

{
    "service": {
        "enabled": true,
        "id": "ee057c",
        "links": {
            "self": "http://identity:35357/v3/services/ee057c"
        },
        "name": "Keystone",
        "type": "identity"
    }
}

Endpoints: /v3/endpoints

Endpoint entities represent URL endpoints for OpenStack web services.

Additional required attributes:

  • service_id (string)

    References the service to which the endpoint belongs.

  • interface (string)

    Describes the visibility of the endpoint according to one of the following values:

    • public: intended for consumption by end users, generally on a publicly available network interface
    • internal: intended for consumption by end users, generally on an unmetered internal network interface
    • admin: intended only for consumption by those needing administrative access to the service, generally on a secure network interface
  • url (string)

    Fully qualified URL of the service endpoint.

Optional attributes:

  • region (string)

    Represents the geographic location of the service endpoint, if relevant to the deployment. The value of this attribute is intended to be implementation specific in meaning.

  • enabled (boolean)

    Setting this value to false prevents the endpoint from appearing in the service catalog.

Example entity:

{
    "endpoint": {
        "enabled": true,
        "id": "6fedc0",
        "interface": "internal",
        "links": {
            "self": "http://identity:35357/v3/endpoints/6fedc0"
        },
        "region": "north",
        "service_id": "ee057c",
        "url": "http://identity:35357/"
    }
}

Tokens

Tokens represent an authenticated user's identity and, potentially, explicit authorization on a specific project or domain.

Tokens are generated by the Identity service via authentication, and may be subsequently validated and/or revoked.

Unlike all other resources in the Identity API, token objects returned by the API do not have id attributes. While token objects do have identifiers, they are not passed in resource URL's nor are they included in the objects themselves. Instead, they are passed in the X-Auth-Token and X-Subject-Token headers, along with a Vary: X-Auth-Token, X-Subject-Token header to inform caches of this pattern.

token objects are only created by the identity service implementation; clients are not expected to create them. Instead, clients provide the service with auth objects in exchange for token objects.

Required attributes:

  • expires_at (string, ISO 8601 extended format date time with microseconds)

    Specifies the expiration time of the token. Once established, a token's expiration may not be changed. A token may be revoked ahead of expiration. If the value represents a time in the past, the token is invalid.

  • issued_at (string, ISO 8601 extended format date time with microseconds)

    Specifies the time at which the token was issued.

  • user (object)

    References the user to which the token belongs.

    Includes the full resource description of a user.

  • methods (list)

    The methods attribute indicates the accumulated set of authentication methods used to obtain the token. For example, if the token was obtained by password authentication, it will contain password. Later, if the token is exchanged using the tokenauthentication method one or more times, the subsequently created tokens will contain both password and token in theirmethods attribute.

    Notice the difference between methods and multifactor authentication. The methods attribute merely indicates the methods used to authenticate the user for the given token. It is up to the client to look for specific methods to determine the total number of factors.

Optional attributes:

  • project (object)

    Specifies a project authorization scope of the token. If this attribute is not provided, then the token is not authorized to access any projects. This attribute must not be included if a domain attribute is included.

    Includes the full resource description of a project.

  • domain (object)

    Specifies a domain authorization scope of the token. This is to provide authorization appropriate to domain-wide APIs, for example user and group management within a domain. Domain authorization does not grant authorization to projects within the domain. If this attribute is not provided, then the token is not authorized to access any domain level resources. This attribute must not be included if a project attribute is included.

    Includes the full resource description of a domain.

  • catalog (object)

    Specifies all endpoints available to/for the token.

    FIXME(dolph): revise with specific expectations.

  • bind (object) New in version 3.1

    Token binding refers to the practice of embedding information from external authentication providers (like a company's Kerberos server) inside the token such that a client may validate that the token is used in conjunction with that authentication mechanism. By coupling this authentication we can prevent re-use of a stolen token as an attacker would not have access to the external authentication.

    Specifies one or more external authorization mechanisms that can be used in conjunction with the token for it to be validated by a bind enforcing client. For example a token may only be used over a Kerberos authenticated connection or with a specific client certificate.

    Includes one or more mechanism identifiers with protocol specific data. The officially supported mechanisms are kerberosand x509 where:

    • The kerberos bind payload is of the form:

      "kerberos": {
          "principal": "USER@REALM"
      }
      

      where the user's Kerberos principal is "USER@REALM".

    • The x509 bind payload is of the form:

      "x509": {
          "fingerprint": "0123456789ABCDEF",
          "algorithm": "sha1"
      }
      

      the fingerprint is the hash of the client certificate to be validated in the specified algorithm. It should be the hex form without seperating spaces or colons. The only supported algorithm is currently sha1.

Example entity:

{
    "token": {
        "expires_at": "2013-02-27T18:30:59.999999Z",
        "issued_at": "2013-02-27T16:30:59.999999Z",
        "methods": [
            "password"
        ],
        "bind": {
            "kerberos": {
                "principal": "USER@REALM"
            }
        },
        "user": {
            "domain": {
                "id": "1789d1",
                "links": {
                    "self": "http://identity:35357/v3/domains/1789d1"
                },
                "name": "example.com"
            }
            "email": "joe@example.com",
            "id": "0ca8f6",
            "links": {
                "self": "http://identity:35357/v3/users/0ca8f6"
            },
            "name": "Joe"
        }
    }
}

Policy

Policies represent arbitrarily serialized policy engine rule sets to be consumed by remote services.

Additional required attributes:

  • blob (string)

    The policy rule set itself, as a serialized blob.

  • type (string)

    The MIME Media Type of the serialized policy blob.

Example entity:

{
    "policy": {
        "blob": "{\"default\": false}",
        "id": "c41a4c",
        "links": {
            "self": "http://identity:35357/v3/policies/c41a4c"
        },
        "type": "application/json"
    }
}

Extensions /v3/extensions

New in version 3.2

Extensions provide a way of adding API functionality to keystone that is not part of the core API. This API provides a way of discovering what extensions are available on a server and the url entry point for each extension.

Additional required attributes:

  • url

    The fully qualified url with which to communicate with the extension.

Optional attributes:

  • name (string)

    A human readable name for the extension.

  • description (string)

    A human readable description of the extension.

  • version (string)

    A string indicating the version of the extension API that is provided.

  • extra (object)

    Additional data that the extension wants to advertise. This information will change between extensions and there is no set format to adhere to. It is assumed that clients who know how to use this extension will also understand the format of extradata. If there is no extra data it is valid for the object to be empty or omitted.

Example entity:

{
    "extension": {
        "id": "OS-MYEXT",
        "name": "My Extension",
        "description": "An Example Extension",
        "version": "v1.0",
        "url": "http://identity:35357/OS-MYEXT"
        "links": {
            "self": "http://identity:35357/v3/extensions/OS-MYEXT",
        }
        "extra": {
            ...
        }
    }
}

Core API

Versions

List versions; GET /

(TBD: This needs additional definition to match the detail below)

Tokens

Use cases:

  • Given a user name & password, get a token to represent the user.
  • Given a token, get a list of other domain/projects the user can access.
  • Given a token, validate the token and return user, domain, project, roles and potential endpoints.
  • Given a valid token, request another token with a different domain/project (change domain/project being represented with the user).
  • Given a valid token, force it's immediate revocation.
Authenticate: POST /auth/tokens

Each request to create a token contains an attribute with identiy information and, optionally, a scope describing the authorization scope being requested. Example request structure:

{
    "auth": {
        "identity": { ... },
        "scope": { ... }
    }
}
Authentication: authentication

Authentication is performed by specifying a list of authentication methods, each with a corresponding object, containing any attributes required by the authentication method. Example request structure for three arbitrary authentication methods:

{
    "auth": {
        "identity": {
            "methods": ["x", "y", "z"],
            "x": { ... },
            "y": { ... },
            "z": { ... }
        }
    }
}
The password authentication method

To authenticate by password, the user must be uniquely identified in addition to providing a password attribute.

The user may be identified by either id or name. A user's id is sufficient to uniquely identify the user. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "id": "0ca8f6",
                    "password": "secrete"
                }
            }
        }
    }
}

If the user is specified by name, then the domain of the user must also be specified in order to uniquely identify the user. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "domain": {
                        "id": "1789d1"
                    },
                    "name": "Joe",
                    "password": "secrete"
                }
            }
        }
    }
}

Alternatively, a domain name may be used to uniquely identify the user. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "domain": {
                        "name": "example.com"
                    },
                    "name": "Joe",
                    "password": "secrete"
                }
            }
        }
    }
}
The token authentication method

If the authenticating user is already in possession of a valid token, then that token is sufficient to identity the user. This method is typically used in combination with request to change authorization scope.

{
    "auth": {
        "identity": {
            "methods": [
                "token"
            ],
            "token": {
                "id": "e80b74"
            }
        }
    }
}
Scope: scope

An authorization scope, including either a project or domain, can be optionally specified as part of the request. If both a domain and a project are specified, an HTTP 400 Bad Request will be returned, as a token cannot be simultaneously scoped to both a project and domain.

project may be specified by either id or name. An id is sufficient to uniquely identify a project. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "id": "0ca8f6",
                    "password": "secrete"
                }
            }
        },
        "scope": {
            "project": {
                "id": "263fd9"
            }
        }
    }
}

If a project is specified by name, then the domain of the project must also be specified in order to uniquely identify theproject. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "id": "0ca8f6",
                    "password": "secrete"
                }
            }
        },
        "scope": {
            "project": {
                "domain": {
                    "id": "1789d1"
                },
                "name": "project-x"
            }
        }
    }
}

Alternatively, a domain name may be used to uniquely identify the project. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "id": "0ca8f6",
                    "password": "secrete"
                }
            }
        },
        "scope": {
            "project": {
                "domain": {
                    "name": "example.com"
                },
                "name": "project-x"
            }
        }
    }
}

If neither a project nor a domain is provided for scope, and the authenticating user has a defined default project (the user'sdefault_project_id attribute), then this will be treated as the preferred authorization scope. If there is no default project defined, then a token will be issued without an explicit scope of authorization.

New in version 3.1 Additionally, if the user's default project is invalid, a token will be issued without an explicit scope of authorization.

Catalog Opt-Out: 'POST /v3/auth/tokens?nocatalog'

New in version 3.1 If the caller specifies a nocatalog query parameter in the authentication request, then the authentication response will not contain the service catalog. The service catalog will otherwise be included in the response by default.

Authentication responses

A response without an explicit authorization scope does not contain a catalogprojectdomain or roles but can be used to uniquely identify the user. Example response:

Headers:
    X-Subject-Token: e80b74

{
    "token": {
        "expires_at": "2013-02-27T18:30:59.999999Z",
        "issued_at": "2013-02-27T16:30:59.999999Z",
        "methods": [
            "password"
        ],
        "user": {
            "domain": {
                "id": "1789d1",
                "links": {
                    "self": "http://identity:35357/v3/domains/1789d1"
                },
                "name": "example.com"
            },
            "id": "0ca8f6",
            "links": {
                "self": "http://identity:35357/v3/users/0ca8f6"
            },
            "name": "Joe"
        }
    }
}

Notice that token ID is not part of the token data. Rather, it is conveyed in the X-Subject-Token header.

A token scoped to a project will also have a service catalog, along with the user's roles applicable to the project. Example response:

Headers: X-Subject-Token

X-Subject-Token: e80b74

{
    "token": {
        "catalog": "FIXME(dolph): need an example here",
        "expires_at": "2013-02-27T18:30:59.999999Z",
        "issued_at": "2013-02-27T16:30:59.999999Z",
        "methods": [
            "password"
        ],
        "project": {
            "domain": {
                "id": "1789d1",
                "links": {
                    "self": "http://identity:35357/v3/domains/1789d1"
                },
                "name": "example.com"
            },
            "id": "263fd9",
            "links": {
                "self": "http://identity:35357/v3/projects/263fd9"
            },
            "name": "project-x"
        },
        "roles": [
            {
                "id": "76e72a",
                "links": {
                    "self": "http://identity:35357/v3/roles/76e72a"
                },
                "name": "admin"
            },
            {
                "id": "f4f392",
                "links": {
                    "self": "http://identity:35357/v3/roles/f4f392"
                },
                "name": "member"
            }
        ],
        "user": {
            "domain": {
                "id": "1789d1",
                "links": {
                    "self": "http://identity:35357/v3/domains/1789d1"
                },
                "name": "example.com"
            },
            "id": "0ca8f6",
            "links": {
                "self": "http://identity:35357/v3/users/0ca8f6"
            },
            "name": "Joe"
        }
    }
}

A token scoped to a domain will also have a service catalog along with the user's roles applicable to the domain. Example response:

Headers: X-Subject-Token

X-Subject-Token: e80b74

{
    "token": {
        "catalog": "FIXME(dolph): need an example here",
        "expires_at": "2013-02-27T18:30:59.999999Z",
        "issued_at": "2013-02-27T16:30:59.999999Z",
        "methods": [
            "password"
        ],
        "domain": {
            "id": "1789d1",
            "links": {
                "self": "http://identity:35357/v3/domains/1789d1"
            },
            "name": "example.com"
        },
        "roles": [
            {
                "id": "76e72a",
                "links": {
                    "self": "http://identity:35357/v3/roles/76e72a"
                },
                "name": "admin"
            },
            {
                "id": "f4f392",
                "links": {
                    "self": "http://identity:35357/v3/roles/f4f392"
                },
                "name": "member"
            }
        ],
        "user": {
            "domain": {
                "id": "1789d1",
                "links": {
                    "self": "http://identity:35357/v3/domains/1789d1"
                },
                "name": "example.com"
            },
            "id": "0ca8f6",
            "links": {
                "self": "http://identity:35357/v3/users/0ca8f6"
            },
            "name": "Joe"
        }
    }
}
Authentication failures

Several authentication errors are possible, including 403 Forbidden and 409 Conflict, but here's an example of an HTTP 401 Unauthorized response:

Status: 401 Not Authorized

{
    "error": {
        "code": 401,
        "message": "The request you have made requires authentication",
        "title": "Not Authorized"
    }
}

Optionally, the Identity service implementation may return an authentication attribute to indicate the supported authentication methods.

Status: 401 Not Authorized

{
    "error": {
        "code": 401,
        "identity": {
            "methods": [
                "password",
                "token",
                "challenge-response"
            ]
        },
        "message": "Need to authenticate with one or more supported methods",
        "title": "Not Authorized"
    }
}

For authentication processes which require multiple round trips, the Identity service implementation may return an HTTP 401 Not Authorized with additional information for the next authentication step.

For example:

Status: 401 Not Authorized

{
    "error": {
        "code": 401,
        "identity": {
            "challenge-response": {
                "challenge": "What was the zip code of your birth place?",
                "session_id": "123456"
            },
            "methods": [
                "challenge-response"
            ]
        },
        "message": "Additional authentications steps required.",
        "title": "Not Authorized"
    }
}
Validate token: GET /auth/tokens

To validate a token using the Identity API, pass your own token in the X-Auth-Token header, and the token to be validated in theX-Subject-Token header. Example request:

Headers:
    X-Auth-Token: 1dd7e3
    X-Subject-Token: c67580

No request body is required.

The Identity service will return the exact same response as when the subject token was issued by POST /auth/tokens.

Check token: HEAD /auth/tokens

This call is identical to GET /auth/tokens, but no response body is provided, even if an error occurs or the token is invalid. A 204 response indicates that the X-Subject-Token is valid.

Revoke token: DELETE /auth/tokens

This call is identical to HEAD /auth/tokens except that the X-Subject-Token token is immediately invalidated, regardless of it'sexpires_at attribute. An additional X-Auth-Token is not required.

Catalog

The key use cases we need to cover:

  • CRUD for services and endpoints
  • Retrieving an endpoint URL by service, region, and interface
List services: GET /services

query filter for "type" (optional)

Response:

Status: 200 OK

{
    "services": [
        {
            "id": "--service-id--",
            "links": {
                "self": "http://identity:35357/v3/services/--service-id--"
            },
            "name": "--service-name",
            "type": "volume"
        },
        {
            "id": "--service-id--",
            "links": {
                "self": "http://identity:35357/v3/services/--service-id--"
            },
            "name": "--service-name",
            "type": "identity"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/services",
        "previous": null,
        "next": null
    }
}
Get service: GET /services/{service_id}

Response:

Status: 200 OK

{
    "service": {
        "id": "--service-id--",
        "links": {
            "self": "http://identity:35357/v3/services/--service-id--"
        },
        "name": "--service-name",
        "type": "volume"
    }
}
Create service: POST /services

Request:

{
    "service": {
        "name": "--optional--",
        "type": "..."
    }
}

Response:

Status: 201 Created

{
    "service": {
        "id": "--service-id--",
        "links": {
            "self": "http://identity:35357/v3/services/--service-id--"
        },
        "name": "--service-name--",
        "type": "volume"
    }
}
Update service: PATCH /services/{service_id}

The request block is the same as the one for create service, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "service": {
        "id": "--service-id--",
        "links": {
            "self": "http://identity:35357/v3/services/--service-id--"
        },
        "name": "--service-name",
        "type": "volume"
    }
}
Delete service: DELETE /services/{service_id}
  • Note: deleting a service when endpoints exist should either 1) delete all associated endpoints or 2) fail until endpoints are deleted

Response:

Status: 204 No Content

Endpoints

List endpoints: GET /endpoints

query filter for "interface" and "service_id" (optional)

Response:

Status: 200 OK

{
    "endpoints": [
        {
            "id": "--endpoint-id--",
            "interface": "public",
            "links": {
                "self": "http://identity:35357/v3/endpoints/--endpoint-id--"
            },
            "name": "the public volume endpoint",
            "region": "--region--",
            "service_id": "--service-id--"
        },
        {
            "id": "--endpoint-id--",
            "interface": "internal",
            "links": {
                "self": "http://identity:35357/v3/endpoints/--endpoint-id--"
            },
            "name": "the internal volume endpoint",
            "region": "--region--",
            "service_id": "--service-id--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/endpoints",
        "previous": null,
        "next": null
    }
}
Create endpoint: POST /endpoints

Request:

{
    "endpoint": {
        "interface": "[admin|public|internal]",
        "name": "name",
        "region": "--optional--",
        "url": "..."
    }
}

Response:

Status: 201 Created

{
    "endpoint": {
        "id": "--endpoint-id--",
        "interface": "internal",
        "links": {
            "self": "http://identity:35357/v3/endpoints/--endpoint-id--"
        },
        "name": "the internal volume endpoint",
        "region": "--region--",
        "service_id": "--service-id--"
    }
}
Update endpoint: PATCH /endpoints/{endpoint_id}

The request block is the same as the one for create endpoint, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "endpoint": {
        "id": "--endpoint-id--",
        "interface": "internal",
        "links": {
            "self": "http://identity:35357/v3/endpoints/--endpoint-id--"
        },
        "name": "the internal volume endpoint",
        "region": "--region--",
        "service_id": "--service-id--"
    }
}
Delete endpoint: DELETE /endpoints/{endpoint_id}

Response:

Status: 204 No Content

Identity

The key use cases we need to cover:

  • CRUD on a user
  • associating a user with a project
  • CRUD on a domain
  • CRUD on a project

Domains

Create domain: POST /domains

Request:

{
    "domain": {
        "description": "--optional--",
        "enabled": true,
        "name": "..."
    }
}

Response:

Status: 201 Created

{
    "domain": {
        "description": "desc of domain",
        "enabled": true,
        "id": "--domain-id--",
        "links": {
            "self": "http://identity:35357/v3/domains/--domain-id--"
        },
        "name": "my domain"
    }
}
List domains: GET /domains

query filter for "name" and "enabled" (optional)

Response:

Status: 200 OK

{
    "domains": [
        {
            "description": "desc of domain",
            "enabled": true,
            "id": "--domain-id--",
            "links": {
                "self": "http://identity:35357/v3/domains/--domain-id--"
            },
            "name": "my domain"
        },
        {
            "description": "desc of another domain",
            "enabled": true,
            "id": "--domain-id--",
            "links": {
                "self": "http://identity:35357/v3/domains/--domain-id--"
            },
            "name": "another domain"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/domains",
        "previous": null,
        "next": null
    }
}
Get domain: GET /domains/{domain_id}

Response:

Status: 200 OK

{
    "domain": {
        "description": "desc of domain",
        "enabled": true,
        "id": "--domain-id--",
        "links": {
            "self": "http://identity:35357/v3/domains/--domain-id--"
        },
        "name": "my domain"
    }
}
Update domain: PATCH /domains/{domain_id}

The request block is the same as the one for create domain, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "domain": {
        "description": "desc of domain",
        "enabled": true,
        "id": "--domain-id--",
        "links": {
            "self": "http://identity:35357/v3/domains/--domain-id--"
        },
        "name": "my domain"
    }
}
Delete domain: DELETE /domains/{domain_id}

Deleting a domain will delete all the entities owned by it (Users, Groups & Projects), as well as any Credentials and Role grants that relate to these entities.

In order to minimize the risk of an inadvertent deletion of a domain and its entities, a domain must first be disabled (using the update domain API) before a successful delete domain API call can be made. Attempting to delete an enabled domain will result in an HTTP 403 Forbidden response.

Response:

Status: 204 No Content

Projects

Create project: POST /projects

Request:

{
    "project": {
        "description": "...",
        "domain_id": "...",
        "enabled": true,
        "name": "..."
    }
}

Response:

Status: 201 Created

{
    "project": {
        "domain_id": "--domain-id--",
        "enabled": true,
        "id": "--project-id--",
        "links": {
            "self": "http://identity:35357/v3/projects/--project-id--"
        },
        "name": "a project name"
    }
}
List projects: GET /projects

query filter for "domain_id", "enabled", "name" (optional)

Response:

Status: 200 OK

{
    "projects": [
        {
            "domain_id": "--domain-id--",
            "enabled": true,
            "id": "--project-id--",
            "links": {
                "self": "http://identity:35357/v3/projects/--project-id--"
            },
            "name": "a project name"
        },
        {
            "domain_id": "--domain-id--",
            "enabled": true,
            "id": "--project-id--",
            "links": {
                "self": "http://identity:35357/v3/projects/--project-id--"
            },
            "name": "another project"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/projects",
        "previous": null,
        "next": null
    }
}
Get project: GET /projects/{project_id}

Response:

Status: 200 OK

{
    "project": {
        "domain_id": "--domain-id--",
        "enabled": true,
        "id": "--project-id--",
        "links": {
            "self": "http://identity:35357/v3/projects/--project-id--"
        },
        "name": "a project name"
    }
}
Update project: PATCH /projects/{project_id}

The request block is the same as the one for create project, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "project": {
        "domain_id": "--domain-id--",
        "enabled": true,
        "id": "--project-id--",
        "links": {
            "self": "http://identity:35357/v3/projects/--project-id--"
        },
        "name": "a project name"
    }
}
Delete project: DELETE /projects/{project_id}
Status: 204 No Content

Users

Create user: POST /users

Request:

{
    "user": {
        "default_project_id": "...",
        "description": "...",
        "domain_id": "--optional--",
        "email": "...",
        "enabled": true,
        "name": "...",
        "password": "--optional--"
    }
}

Response:

Status: 201 Created

{
    "user": {
        "default_project_id": "--default-project-id--",
        "description": "a user",
        "domain_id": "1789d1",
        "email": "...",
        "enabled": true,
        "id": "--user-id--",
        "links": {
            "self": "http://identity:35357/v3/users/--user-id--"
        },
        "name": "admin"
    }
}
List users: GET /users

query filter for "domain_id", "email", "enabled", "name" (optional)

Response:

Status: 200 OK

{
    "users": [
        {
            "default_project_id": "--default-project-id--",
            "description": "a user",
            "domain_id": "1789d1",
            "email": "...",
            "enabled": true,
            "id": "--user-id--",
            "links": {
                "self": "http://identity:35357/v3/users/--user-id--"
            },
            "name": "admin"
        },
        {
            "default_project_id": "--default-project-id--",
            "description": "another user",
            "domain_id": "1789d1",
            "email": "...",
            "enabled": true,
            "id": "--user-id--",
            "links": {
                "self": "http://identity:35357/v3/users/--user-id--"
            },
            "name": "someone"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/users",
        "previous": null,
        "next": null
    }
}
Get user: GET /users/{user_id}

Response:

Status: 200 OK

{
    "user": {
        "default_project_id": "--default-project-id--",
        "description": "a user",
        "domain_id": "1789d1",
        "email": "...",
        "enabled": true,
        "id": "--user-id--",
        "links": {
            "self": "http://identity:35357/v3/users/--user-id--"
        },
        "name": "admin"
    }
}
List user projects: GET /users/{user_id}/projects

query filter for "name", "enabled" on project resources (optional)

Response:

Status: 200 OK

{
    "projects": [
        {
            "domain_id": "--domain-id--",
            "enabled": true,
            "id": "--project-id--",
            "links": {
                "self": "http://identity:35357/v3/projects/--project-id--"
            },
            "name": "a project name"
        },
        {
            "domain_id": "--domain-id--",
            "enabled": true,
            "id": "--project-id--",
            "links": {
                "self": "http://identity:35357/v3/projects/--project-id--"
            },
            "name": "another domain"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/users/--user-id--/projects",
        "previous": null,
        "next": null
    }
}
List groups of which a user is a member: GET /users/{user_id}/groups

query filter for "name" (optional)

Response:

Status: 200 OK

{
    "groups": [
        {
            "description": "Developers cleared for work on all general projects"
            "domain_id": "--domain-id--",
            "id": "--group-id--",
            "links": {
                "self": "http://identity:35357/v3/groups/--group-id--"
            },
            "name": "Developers"
        },
        {
            "description": "Developers cleared for work on secret projects"
            "domain_id": "--domain-id--",
            "id": "--group-id--",
            "links": {
                "self": "http://identity:35357/v3/groups/--group-id--"
            },
            "name": "Secure Developers"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/users/--user-id--/groups",
        "previous": null,
        "next": null
    }
}
Update user: PATCH /users/{user_id}

The request block is the same as the one for create user, except that only the attributes that are being updated need to be included. Use this method attempt to update user password or enable/disable the user. This may return a HTTP 501 Not Implemented if the back-end driver doesn't allow for the functionality.

Response:

Status: 200 OK

{
    "user": {
        "default_project_id": "--default-project-id--",
        "description": "a user",
        "domain_id": "1789d1",
        "email": "...",
        "enabled": true,
        "id": "--user-id--",
        "links": {
            "self": "http://identity:35357/v3/users/--user-id--"
        },
        "name": "admin"
    }
}
Delete user: DELETE /users/{user_id}

Response:

Status: 204 No Content

Groups

Create group: POST /groups

Request:

{
    "group": {
        "description": "--optional--",
        "domain_id": "--optional--",
        "name": "..."
    }
}

Response:

Status: 201 Created

{
    "group": {
        "description": "Developers cleared for work on secret projects",
        "id": "--group-id--",
        "links": {
            "self": "http://identity:35357/v3/groups/--group-id--"
        },
        "name": "Secure Developers"
    }
}
List groups: GET /groups

query filter for "domain_id", "name" (optional)

Response:

Status: 200 OK

{
    "groups": [
        {
            "description": "Developers cleared for work on all general projects"
            "domain_id": "--domain-id--",
            "id": "--group-id--",
            "links": {
                "self": "http://identity:35357/v3/groups/--group-id--"
            },
            "name": "Developers"
        },
        {
            "description": "Developers cleared for work on secret projects"
            "domain_id": "--domain-id--",
            "id": "--group-id--",
            "links": {
                "self": "http://identity:35357/v3/groups/--group-id--"
            },
            "name": "Secure Developers"
        },
        {
            "description": "Testers cleared for work on all general projects"
            "domain_id": "--domain-id--",
            "id": "--group-id--",
            "links": {
                "self": "http://identity:35357/v3/groups/--group-id--"
            },
            "name": "Testers"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/groups",
        "previous": null,
        "next": null
    }
}
Get group: GET /groups/{group_id}

Response:

Status: 200 OK

{
    "group": {
        "description": "Developers cleared for work on secret projects",
        "id": "--group-id--",
        "links": {
            "self": "http://identity:35357/v3/groups/--group-id--"
        },
        "name": "Secure Developers"
    }
}
List users who are members of a group: GET /groups/{group_id}/users

query filter for "name", "enabled", "email" (optional)

Response:

Status: 200 OK

{
    "users": [
        {
            "default_project_id": "--default-project-id--",
            "description": "a user",
            "domain_id": "--domain-id--",
            "email": "...",
            "enabled": true,
            "id": "--user-id--",
            "links": {
                "self": "http://identity:35357/v3/users/--user-id--"
            },
            "name": "admin"
        },
        {
            "default_project_id": "--default-project-id--",
            "description": "another user",
            "domain_id": "--domain-id--",
            "email": "...",
            "enabled": true,
            "id": "--user-id--",
            "links": {
                "self": "http://identity:35357/v3/users/--user-id--"
            },
            "name": "someone"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/groups/--group-id--/users",
        "previous": null,
        "next": null
    }
}
Update group: PATCH /groups/{group_id}

The request block is the same as the one for create group, except that only the attributes that are being updated need to be included. This may return a HTTP 501 Not Implemented if the back-end driver doesn't allow for the functionality.

Response:

Status: 200 OK

{
    "group": {
        "description": "Developers cleared for work on secret projects",
        "id": "--group-id--",
        "links": {
            "self": "http://identity:35357/v3/groups/--group-id--"
        },
        "name": "Secure Developers"
    }
}
Delete group: DELETE /groups/{group_id}

Response:

Status: 204 No Content
Add user to group: PUT /groups/{group_id}/users/{user_id}

Response:

Status: 204 No Content
Remove user from group: DELETE /groups/{group_id}/users/{user_id}

Response:

Status: 204 No Content
Check if user is member of group: HEAD /groups/{group_id}/users/{user_id}

Response:

Status: 204 No Content

Credentials

The key use cases we need to cover:

  • CRUD on a credential
Create credential: POST /credentials

This example shows creating an EC2 style credential where the credentials are a combination of access_key and secret. Other credentials (such as access_key) may be supported by simply changing the content of the key data.

Request:

{
    "credential": {
        "blob": "--JSON serialized object containing 'access' and 'secret'--",
        "project_id": "--optional--",
        "type": "ec2",
        "user_id": "--user--id--"
    }
}

Response:

Status: 201 Created

{
    "credential": {
        "blob": "--JSON serialized object containing 'access' and 'secret'--",
        "id": "--credential-id--",
        "links": {
            "self": "http://identity:35357/v3/credentials/--credential-id--"
        },
        "project_id": "--project-id--",
        "type": "ec2",
        "user_id": "--user--id--"
    }
}
List credentials: GET /credentials

Response:

Status: 200 OK

{
    "credentials": [
        {
            "blob": "--JSON serialized object containing 'access' and 'secret'--",
            "id": "--credential-id--",
            "links": {
                "self": "http://identity:35357/v3/credentials/--credential-id--"
            },
            "project_id": "--project-id--",
            "type": "ec2",
            "user_id": "--user--id--"
        },
        {
            "blob": "--JSON serialized object containing 'access' and 'secret'--",
            "id": "--credential-id--",
            "links": {
                "self": "http://identity:35357/v3/credentials/--credential-id--"
            },
            "project_id": "--project-id--",
            "type": "ec2",
            "user_id": "--user--id--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/credentials",
        "previous": null,
        "next": null
    }
}
Get credential: GET /credentials/{credential_id}

Response:

Status: 200 OK

{
    "credential": {
        "blob": "--JSON serialized object containing 'access' and 'secret'--",
        "id": "--credential-id--",
        "links": {
            "self": "http://identity:35357/v3/credentials/--credential-id--"
        },
        "project_id": "--project-id--",
        "type": "ec2",
        "user_id": "--user--id--"
    }
}
Update credential: PATCH /credentials/{credential_id}

The request block is the same as the one for create credential, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "credential": {
        "blob": "--JSON serialized object containing 'access' and 'secret'--",
        "id": "--credential-id--",
        "links": {
            "self": "http://identity:35357/v3/credentials/--credential-id--"
        },
        "project_id": "--project-id--",
        "type": "ec2",
        "user_id": "--user--id--"
    }
}
Delete credential: DELETE /credentials/{credential_id}

Response:

Status: 204 No Content

Roles

The key use cases we need to cover:

  • CRUD on a role
  • Associating a role with a project or domain
Create role: POST /roles

Request:

{
    "role": {
        "name": "..."
    }
}

Response:

Status: 201 Created

{
    "role": {
        "id": "--role-id--",
        "links": {
            "self": "http://identity:35357/v3/roles/--role-id--"
        },
        "name": "a role name"
    }
}
List roles: GET /roles

query filter for "name" (optional)

Response:

Status: 200 OK

{
    "roles": [
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "a role name"
        },
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "a role name"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/roles",
        "previous": null,
        "next": null
    }
}
Get role: GET /roles/{role_id}

Response:

Status: 200 OK

{
    "role": {
        "id": "--role-id--",
        "links": {
            "self": "http://identity:35357/v3/roles/--role-id--"
        },
        "name": "a role name"
    }
}
Update role: PATCH /roles/{role_id}

The request block is the same as the one for create role, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "role": {
        "id": "--role-id--",
        "links": {
            "self": "http://identity:35357/v3/roles/--role-id--"
        },
        "name": "a role name"
    }
}
Delete role: DELETE /roles/{role_id}

Response:

Status: 204 No Content
Grant role to user on domain: PUT /domains/{domain_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Grant role to group on domain:PUT /domains/{domain_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
List user's roles on domain: GET /domains/{domain_id}/users/{user_id}/roles

Response:

Status: 200 OK

{
    "roles": [
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--",
        },
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/domains/--domain_id--/users/--user_id--/roles",
        "previous": null,
        "next": null
    }
}
List group's roles on domain: GET /domains/{domain_id}/groups/{group_id}/roles

Response:

Status: 200 OK

{
    "roles": [
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--",
        },
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/domains/--domain_id--/groups/--group_id--/roles",
        "previous": null,
        "next": null
    }
}
Check if user has role on domain:HEAD /domains/{domain_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Check if group has role on domain:HEAD /domains/{domain_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
Revoke role from user on domain:DELETE /domains/{domain_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Revoke role from group on domain:DELETE /domains/{domain_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
Grant role to user on project:PUT /projects/{project_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Grant role to group on project:PUT /projects/{project_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
List user's roles on project: GET /projects/{project_id}/users/{user_id}/roles

Response:

Status: 200 OK

{
    "roles": [
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--",
        },
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/projects/--project_id--/users/--user_id--/roles",
        "previous": null,
        "next": null
    }
}
List group's roles on project: GET /projects/{project_id}/groups/{group_id}/roles

Response:

Status: 200 OK

{
    "roles": [
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--",
        },
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/projects/--project_id--/groups/--group_id--/roles",
        "previous": null,
        "next": null
    }
}
Check if user has role on project:HEAD /projects/{project_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Check if group has role on project:HEAD /projects/{project_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
Revoke role from user on project:DELETE /projects/{project_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Revoke role from group on project:DELETE /projects/{project_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
List effective role assignments: GET /role_assignments

query_filter: group.id, role.id, scope.domain.id, scope.project.id, user.id (all optional)
query_string: effective (optional, default false)

Get a list of role assignments. This API is only available from v3.1 onwards.

If no query filters are specified, then this API will return a list of all role assignments.

Response:

Status: 200 OK

{
    "role_assignments": [
        {
            "links": {
                "assignment": "http://identity:35357/v3/domains/--domain-id--/users/--user-id--/roles/--role-id--"
            },
            "role": {
                "id": "--role-id--"
            },
            "scope": {
                "domain": {
                    "id": "--domain-id--"
                }
            },
            "user": {
                "id": "--user-id--"
            }
        },
        {
            "group": {
                "id": "--group-id--"
            },
            "links": {
                "assignment": "http://identity:35357/v3/projects/--project-id--/groups/--group-id--/roles/--role-id--"
            },
            "role": {
                "id": "--role-id--"
            },
            "scope": {
                "project": {
                    "id": "--project-id--"
                }
            }
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/role_assignments",
        "previous": null,
        "next": null
    }
}

Since this list is likely to be very long, this API would typically always be used with one of more of the filter queries. Some typical examples are:

GET /role_assignments?user.id={user_id} would list all role assignments involving the specified user.

GET /role_assignments?scope.project.id={project_id} would list all role assignments involving the specified project.

Each role assignment entity in the collection contains a link to the assignment that gave rise to this entity.

If the query_string effective is specified then, rather than simply returning a list of role assignments that have been made, the API returns a list of effective assignments at the user, project and domain level, having allowed for the effects of group membership. Since the effects of group membership have already been allowed for, the group role assignment entities themselves will not be returned in the collection. This represents the effective role assignments that would be included in a scoped token. The same set of query filters can also be used with the effective query string. For example:

GET /role_assignments?user.id={user_id}&effective would, in other words, answer the question "what can this user actually do?".

GET /role_assignments?user.id={user_id}&scope.project.id={project_id}&effective would return the equivalent set of role assignments that would be included in the token response of a project scoped token.

An example response for an API call with the query_string effective specified is given below:

Response:

Status: 200 OK

{
    "role_assignments": [
        {
            "links": {
                "assignment": "http://identity:35357/v3/domains/--domain-id--/users/--user-id--/roles/--role-id--"
            },
            "role": {
                "id": "--role-id--"
            },
            "scope": {
                "domain": {
                    "id": "--domain-id--"
                }
            },
            "user": {
                "id": "--user-id--"
            }
        },
        {
            "links": {
                "assignment": "http://identity:35357/v3/projects/--project-id--/groups/--group-id--/roles/--role-id--",
                "membership": "http://identity:35357/v3/groups/--group-id--/users/--user-id--"
            },
            "role": {
                "id": "--role-id--"
            },
            "scope": {
                "project": {
                    "id": "--project-id--"
                }
            },
            "user": {
                "id": "--user-id--"
            }
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/role_assignments?effective",
        "previous": null,
        "next": null
    }
}

The entity links section of a response using the effective query string also contains, for entities that are included by virtue of group memebership, a url that can be used to access the membership of the group.

Policies

The key use cases we need to cover:

  • CRUD on a policy
Create policy: POST /policies

Request:

{
    "blob": "--serialized-blob--",
    "type": "--serialization-mime-type--"
}

Response:

Status: 201 Created

{
    "policy": {
        "blob": "--serialized-blob--",
        "id": "--policy-id--",
        "links": {
            "self": "http://identity:35357/v3/policies/--policy-id--"
        },
        "type": "--serialization-mime-type--"
    }
}
List policies: GET /policies

query filter for "type" (optional)

Response:

Status: 200 OK

{
    "policies": [
        {
            "blob": "--serialized-blob--",
            "id": "--policy-id--",
            "links": {
                "self": "http://identity:35357/v3/policies/--policy-id--"
            },
            "type": "--serialization-mime-type--"
        },
        {
            "blob": "--serialized-blob--",
            "id": "--policy-id--",
            "links": {
                "self": "http://identity:35357/v3/policies/--policy-id--"
            },
            "type": "--serialization-mime-type--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/policies",
        "previous": null,
        "next": null
    }
}
Get policy: GET /policies/{policy_id}

Response:

Status: 200 OK

{
    "policy": {
        "blob": "--serialized-blob--",
        "id": "--policy-id--",
        "links": {
            "self": "http://identity:35357/v3/policies/--policy-id--"
        },
        "type": "--serialization-mime-type--"
    }
}
Update policy: PATCH /policies/{policy_id}

The request block is the same as the one for create policy, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "policy": {
        "blob": "--serialized-blob--",
        "id": "--policy-id--",
        "links": {
            "self": "http://identity:35357/v3/policies/--policy-id--"
        },
        "type": "--serialization-mime-type--"
    }
}
Delete policy: DELETE /policies/{policy_id}

Response:

Status: 204 No Content

Extensions

The key use cases we need to cover:

  • CRUD on a extension
List extensions: GET /extensions

Response:

Status: 200 OK

{
    "extensions": [
        {
            "id": "--extension-id--",
            "name": "--extension-name--",
            "url": "http://identity:35357/--extension-root--"
            "links": {
                "self": "http://identity:35357/v3/extensions/--extension-id--",
            },
            "extra": {
                "features": ["feature1", "feature5"],
            }
        },
        {
            "id": "--extension-id--",
            "version": "--extension-version--",
            "url": "http://identity:35357/--extension-root--/--extension-version--"
            "links": {
                "self": "http://identity:35357/v3/extensions/--extension-id--",
            },
        },
    ],
    "links": {
        "self": "http://identity:35357/v3/extensions",
        "previous": null,
        "next": null
    }
}
Get extension: GET /extensions/{extension_id}

Response:

Status: 200 OK

{
    "extension": {
        "id": "--extension-id--",
        "name": "--extension-name--",
        "url": "http://identity:35357/--extension-root--"
        "links": {
            "self": "http://identity:35357/v3/extensions/--extension-id--",
        },
    },
}

What's New in Version 3.0

  • Former "Service" and "Admin" APIs (including CRUD operations previously defined in the v2 OS-KSADM extension) are consolidated into a single core API
  • "Tenants" are now known as "projects"
  • "Groups": a container representing a collection of users
  • "Domains": a high-level container for projects, users and groups
  • "Policies": a centralized repository for policy engine rule sets
  • "Credentials": generic credential storage per user (e.g. EC2, PKI, SSH, etc.)
  • Roles can be granted at either the domain or project level
  • User, group and project names only have to be unique within their owning domain
  • Retrieving your list of projects (previously GET /tenants) is now explicitly based on your user ID:GET /users/{user_id}/projects
  • Tokens explicitly represent user+project or user+domain pairs
  • Partial updates are performed using the HTTP PATCH method
  • Token ID values no longer appear in URLs
This repository
 branch: master
 
  file  3062 lines (2367 sloc)  83.778 kb

OpenStack Identity API v3

The Identity API primarily fulfills authentication and authorization needs within OpenStack, and is intended to provide a programmatic facade in front of existing authentication and authorization system(s).

The Identity API also provides endpoint discovery through a service catalog, identity management, project management, and a centralized repository for policy engine rule sets.

What's New in Version 3.2

  • Extension Discovery.

What's New in Version 3.1

  • A token without an explicit scope of authorization is issued if the user does not specify a project and does not have authorization on the project specified by their default project attribute.
  • Introduced a generalized call for getting role assignments, with filtering for user, group, project, domain and role.
  • Introduced a mechanism to opt-out from catalog information during token creation
  • Added optional bind information to token structure.

What's New in Version 3.0

  • Former "Service" and "Admin" APIs (including CRUD operations previously defined in the v2 OS-KSADM extension) are consolidated into a single core API
  • "Tenants" are now known as "projects"
  • "Groups": a container representing a collection of users
  • "Domains": a high-level container for projects, users and groups
  • "Policies": a centralized repository for policy engine rule sets
  • "Credentials": generic credential storage per user (e.g. EC2, PKI, SSH, etc.)
  • Roles can be granted at either the domain or project level
  • User, group and project names only have to be unique within their owning domain
  • Retrieving your list of projects (previously GET /tenants) is now explicitly based on your user ID:GET /users/{user_id}/projects
  • Tokens explicitly represent user+project or user+domain pairs
  • Partial updates are performed using the HTTP PATCH method
  • Token ID values no longer appear in URLs

API Conventions

The Identity API provides a RESTful JSON interface.

Each REST resource contains a canonically unique identifier (ID) defined by the Identity service implementation and is provided as the id attribute; Resource ID's are strings of non-zero length.

The resource paths of all collections are plural and are represented at the root of the API (e.g. /v3/policies).

TCP port 35357 is designated by the Internet Assigned Numbers Authority ("IANA") for use by OpenStack Identity services. Example API requests & responses in this document therefore assume that the Identity service implementation is deployed at the root ofhttp://identity:35357/.

Required Attributes

Headers:

  • X-Auth-Token

    This header is used to convey the authentication token when accessing Identity APIs.

  • X-Subject-Token

    This header is used to convey the subject of the request for token-related operations.

For collections:

  • links (object)

    Specifies a list of relational links to the collection.

    • self (url)

    A self-relational link provided as an absolute URL. This attribute is provided by the identity service implementation.

    • previous (url)

    A relational link to the previous page of the list, provided as an absolute URL. This attribute is provided by the identity service implementation. May be null.

    • next (url)

    A relational to the next page of the list, provided as an absolute URL. This attribute is provided by the identity service implementation. May be null.

For members:

  • id (string)

    Globally unique resource identifier. This attribute is provided by the identity service implementation.

  • links (object)

    Specifies a set of relational links relative to the collection member.

    • self (url)

    A self-relational link provided as an absolute URL. This attribute is provided by the identity service implementation.

CRUD Operations

Unless otherwise documented (tokens being the notable exception), all resources provided by the Identity API support basic CRUD operations (create, read, update, delete).

The examples in this section utilize a resource collection of Entities on /v3/entities which is not actually a part of the Identity API, and is used for illustrative purposes only.

Create an Entity

When creating an entity, you must provide all required attributes (except those provided by the Identity service implementation, such as the resource ID):

Request:

POST /entities

{
    "entity": {
        "name": string,
        "description": string,
        "enabled": boolean
    }
}

The full entity is returned in a successful response (including the new resource's ID and a self-relational link), keyed by the singular form of the resource name:

201 Created

{
    "entity": {
        "id": string,
        "name": string,
        "description": string,
        "enabled": boolean,
        "links": {
            "self": url
        }
    }
}
List Entities

Request the entire collection of entities:

GET /entities

A successful response includes a list of anonymous dictionaries, keyed by the plural form of the resource name (identical to that found in the resource URL):

200 OK

{
    "entities": [
        {
            "id": string,
            "name": string,
            "description": string,
            "enabled": boolean,
            "links": {
                "self": url
            }
        },
        {
            "id": string,
            "name": string,
            "description": string,
            "enabled": boolean,
            "links": {
                "self": url
            }
        }
    ],
    "links": {
        "self": url,
        "next": url,
        "previous": url
    }
}
List Entities filtered by attribute

Beyond each resource's canonically unique identifier (the id attribute), not all attributes are guaranteed unique on their own. To list resources which match a specified attribute value, we can perform a filter query using a query string with one or more attribute/value pairs:

GET /entities?name={entity_name}&enabled={entity_enabled}

The response is a subset of the full collection:

200 OK

{
    "entities": [
        {
            "id": string,
            "name": string,
            "description": string,
            "enabled": boolean,
            "links": {
                "self": url
            }
        }
    ],
    "links": {
        "self": url,
        "next": url,
        "previous": url
    }
}
Get an Entity

Request a specific entity by ID:

GET /entities/{entity_id}

The full resource is returned in response:

200 OK

{
    "entity": {
        "id": string,
        "name": string,
        "description": string,
        "enabled": boolean,
        "links": {
            "self": url
        }
    }
}
Nested collections

An entity may contain nested collections, in which case the required attributes for collections still apply; however, to avoid conflicts with other required attributes, the required attributes of the collection are prefixed with the name of the collection. For example, if anentity contains a nested collection of objects, the links for the collection of objects is called objects_links:

{
    "entity": {
        "id": string,
        "name": string,
        "description": string,
        "enabled": boolean,
        "links": {
            "self": url
        },
        "objects": [
            {
                "id": string,
                "name": string,
                "description": string,
                "enabled": boolean,
                "links": {
                    "self": url
                }
            }
        ],
        "objects_links": {
            "self": url,
            "next": url,
            "previous": url
        }
    }
}
Update an Entity

Partially update an entity (unlike a standard PUT operation, only the specified attributes are replaced):

PATCH /entities/{entity_id}

{
    "entity": {
        "description": string
    }
}

The full entity is returned in response:

200 OK

{
    "entity": {
        "id": string,
        "name": string,
        "description": string,
        "enabled": boolean,
        "links": {
            "self": url
        }
    }
}
Delete an Entity

Delete a specific entity by ID:

DELETE /entities/{entity_id}

A successful response does not include a body:

204 No Content

HTTP Status Codes

The Identity API uses a subset of the available HTTP status codes to communicate specific success and failure conditions to the client.

200 OK

This status code is returned in response to successful GET and PATCH operations.

201 Created

This status code is returned in response to successful POST operations.

204 No Content

This status code is returned in response to successful HEADPUT and DELETE operations.

300 Multiple Choices

This status code is returned by the root identity endpoint, with references to one or more Identity API versions (such as /v3/).

400 Bad Request

This status code is returned when the Identity service fails to parse the request as expected. This is most frequently returned when a required attribute is missing, a disallowed attribute is specified (such as an id on POST in a basic CRUD operation), or an attribute is provided of an unexpected data type.

The client is assumed to be in error.

401 Unauthorized

This status code is returned when either authentication has not been performed, the provided X-Auth-Token is invalid or authentication credentials are invalid (including the user, project or domain having been disabled).

403 Forbidden

This status code is returned when the request is successfully authenticated but not authorized to perform the requested action.

404 Not Found

This status code is returned in response to failed GETHEADPOSTPUTPATCH and DELETE operations when a referenced entity cannot be found by ID. In the case of a POST request, the referenced entity may be in the request body as opposed to the resource path.

409 Conflict

This status code is returned in response to failed POST and PATCH operations. For example, when a client attempts to update an entity's unique attribute which conflicts with that of another entity in the same collection.

Alternatively, a client should expect this status code when attempting to perform the same create operation twice in a row on a collection with a user-defined and unique attribute. For example, a User's name attribute is defined to be unique and user-defined, so making the same POST /users request twice in a row will result in this status code.

The client is assumed to be in error.

500 Internal Server Error

This status code is returned when an unexpected error has occurred in the Identity service implementation.

501 Not Implemented

This status code is returned when the Identity service implementation is unable to fulfill the request because it is incapable of implementing the entire API as specified.

For example, an Identity service may be incapable of returning an exhaustive collection of Projects with any reasonable expectation of performance, or lack the necessary permission to create or modify the collection of users (which may be managed by a remote system); the implementation may therefore choose to return this status code to communicate this condition to the client.

503 Service Unavailable

This status code is returned when the Identity service is unable to communicate with a backend service, or by a proxy in front of the Identity service unable to communicate with the Identity service itself.

API Resources

Users: /v3/users

User entities represent individual API consumers and are owned by a specific domain.

Role grants explicitly associate users with projects or domains. Each user-project or user-domain pair can have a unique set of roles granted on them.

A user without any role grants is effectively useless from the perspective of an OpenStack service and should never have access to any resources. It is allowed, however, as a means of acquiring or loading users from external sources prior to mapping them to projects.

Additional required attributes:

  • name (string)

    Either globally or domain unique username, depending on owning domain.

Optional attributes:

  • domain_id (string)

    References the domain which owns the user; if a domain is not specified by the client, the Identity service implementation will default it to the domain to which the client's token is scoped.

  • default_project_id (string)

    References the user's default project against which to authorize, if the API user does not explicitly specify one when creating a token. Setting this attribute does not grant any actual authorization on the project, and is merely provided for the user's convenience. Therefore, the referenced project does not need to exist within the user's domain.

    New in version 3.1 If the user does not have authorization to their default project, the default project will be ignored at token creation.

  • description (string)

  • enabled (boolean)

    Setting this value to false prevents the user from authenticating or receiving authorization. Additionally, all pre-existing tokens held by the user are immediately invalidated. Re-enabling a user does not re-enable pre-existing tokens.

  • password (string)

    The default form of credential used during authentication.

Example entity:

{
    "user": {
        "default_project_id": "263fd9",
        "domain_id": "1789d1",
        "email": "joe@example.com",
        "enabled": true,
        "id": "0ca8f6",
        "links": {
            "self": "http://identity:35357/v3/users/0ca8f6"
        },
        "name": "Joe"
    }
}

Groups: /v3/groups

Group entities represent a collection of Users and are owned by a specific domain. As with individual users, role grants explicitly associate groups with projects or domains. A group role grant onto a project/domain is the equivalent of granting each individual member of the group the role on that project/domain. Once a group role grant has been made, the addition or removal of a user to such a group will result in the automatic granting/revoking of that role to the user, which will also cause any token containing that user and project/domain to be revoked.

As with users, a group entity without any role grants is effectively useless from the perspective an OpenStack service and should never have access to any resources. It is allowed, however, as a means of acquiring or loading users/groups from external sources prior to mapping them to projects/domains.

Additional required attributes:

  • name (string)

    Unique group name, within the owning domain.

Optional attributes:

  • domain_id (string)

    References the domain which owns the group; if a domain is not specified by the client, the Identity service implementation will default it to the domain to which the client's token is scoped.

  • description (string)

Example entity:

{
    "group": {
        "description": "Developers cleared for work on all general projects"
        "domain_id": "1789d1",
        "id": "70febc",
        "links": {
            "self": "http://identity:35357/v3/groups/70febc"
        },
        "name": "Developers"
    }
}

Credentials: /v3/credentials

Credentials represent arbitrary authentication credentials associated with a user. A user may have zero or more credentials, each optionally scoped to a specific project.

Additional required attributes:

  • user_id (string)

    References the user which owns the credential.

  • type (string)

    Representing the credential type, such as ec2 or cert. A specific implementation may determine the list of supported types.

  • blob (blob)

    Arbitrary blob of the credential data, to be parsed according to the type.

Optional attributes:

  • project_id (string)

    References a project which limits the scope the credential applies to.

Example entity:

{
    "credential": {
        "blob": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
        "id": "80239a",
        "links": {
            "self": "http://identity:35357/v3/credentials/80239a"
        },
        "project_id": "263fd9",
        "type": "ec2",
        "user_id": "0ca8f6"
    }
}

Projects: /v3/projects

Projects represent the base unit of "ownership" in OpenStack, in that all resources in OpenStack should be owned by a specific project ("projects" were also formerly known as "tenants"). A project itself must be owned by a specific domain.

Required attributes:

  • name (string)

    Either globally or domain unique project name, depending on owning domain.

Optional attributes:

  • domain_id (string)

    References the domain which owns the project; if a domain is not specified by the client, the Identity service implementation will default it to the domain to which the client's token is scoped.

  • description (string)

  • enabled (boolean)

    Setting this attribute to false prevents users from authorizing against this project. Additionally, all pre-existing tokens authorized for the project are immediately invalidated. Re-enabling a project does not re-enable pre-existing tokens.

Example entity:

{
    "project": {
        "domain_id": "1789d1",
        "enabled": true,
        "id": "263fd9",
        "name": "project-x",
        "links": {
            "self": "http://identity:35357/v3/projects/263fd9"
        }
    }
}

Domains: /v3/domains

Domains represent collections of users, groups and projects. Each is owned by exactly one domain. Users, however, can be associated with multiple projects by granting roles to the user on a project (including projects owned by other domains).

Each domain defines a namespace in which certain API-visible name attributes exist, which affects whether those names need to be globally unique or simply unique within that domain. Within the Identity API, there are five such name attributes, whose uniqueness are affected by the domain:

  • Domain Name: This is always globally unique across all domains.

  • Role Name: This is always globally unique across all domains.

  • User Name: This is only unique within the owning domain.

  • Project Name: This is only unique within the owning domain.

  • Group Name: This is only unique within the owning domain.

Additional required attributes:

  • name (string)

    Globally unique name.

Optional attributes:

  • description (string)

  • enabled (boolean)

    Setting this attribute to false prevents users from authorizing against this domain or any projects owned by this domain, and prevents users owned by this domain from authenticating or receiving any other authorization. Additionally, all pre-existing tokens applicable to the above entities are immediately invalidated. Re-enabling a domain does not re-enable pre-existing tokens.

Example entity:

{
    "domain": {
        "enabled": true,
        "id": "1789d1",
        "links": {
            "self": "http://identity:35357/v3/domains/1789d1"
        },
        "name": "example.com"
    }
}

Roles: /v3/roles/

Roles entities are named identifiers used to map a collection of actions from a user to either a specific project or across an entire domain.

Additional required attributes:

  • name (string)

    Globally unique name of the role.

Example entity:

{
    "role": {
        "id": "76e72a",
        "links": {
            "self": "http://identity:35357/v3/roles/76e72a"
        },
        "name": "admin"
    }
}

Services: /v3/services

Service entities represent web services in the OpenStack deployment. A service may have zero or more endpoints associated with it, although a service with zero endpoints is essentially useless in an OpenStack configuration.

Additional required attributes:

  • type (string)

    Describes the API implemented by the service. The following values are recognized within the OpenStack ecosystem:computeimageec2identityvolumenetwork. To support non-core and future projects, the value should not be validated against this list.

Optional attributes:

  • name (string)

    User-facing name of the service.

  • enabled (boolean)

    Setting this value to false prevents the service and its endpoints from appearing in the service catalog.

Example entity:

{
    "service": {
        "enabled": true,
        "id": "ee057c",
        "links": {
            "self": "http://identity:35357/v3/services/ee057c"
        },
        "name": "Keystone",
        "type": "identity"
    }
}

Endpoints: /v3/endpoints

Endpoint entities represent URL endpoints for OpenStack web services.

Additional required attributes:

  • service_id (string)

    References the service to which the endpoint belongs.

  • interface (string)

    Describes the visibility of the endpoint according to one of the following values:

    • public: intended for consumption by end users, generally on a publicly available network interface
    • internal: intended for consumption by end users, generally on an unmetered internal network interface
    • admin: intended only for consumption by those needing administrative access to the service, generally on a secure network interface
  • url (string)

    Fully qualified URL of the service endpoint.

Optional attributes:

  • region (string)

    Represents the geographic location of the service endpoint, if relevant to the deployment. The value of this attribute is intended to be implementation specific in meaning.

  • enabled (boolean)

    Setting this value to false prevents the endpoint from appearing in the service catalog.

Example entity:

{
    "endpoint": {
        "enabled": true,
        "id": "6fedc0",
        "interface": "internal",
        "links": {
            "self": "http://identity:35357/v3/endpoints/6fedc0"
        },
        "region": "north",
        "service_id": "ee057c",
        "url": "http://identity:35357/"
    }
}

Tokens

Tokens represent an authenticated user's identity and, potentially, explicit authorization on a specific project or domain.

Tokens are generated by the Identity service via authentication, and may be subsequently validated and/or revoked.

Unlike all other resources in the Identity API, token objects returned by the API do not have id attributes. While token objects do have identifiers, they are not passed in resource URL's nor are they included in the objects themselves. Instead, they are passed in the X-Auth-Token and X-Subject-Token headers, along with a Vary: X-Auth-Token, X-Subject-Token header to inform caches of this pattern.

token objects are only created by the identity service implementation; clients are not expected to create them. Instead, clients provide the service with auth objects in exchange for token objects.

Required attributes:

  • expires_at (string, ISO 8601 extended format date time with microseconds)

    Specifies the expiration time of the token. Once established, a token's expiration may not be changed. A token may be revoked ahead of expiration. If the value represents a time in the past, the token is invalid.

  • issued_at (string, ISO 8601 extended format date time with microseconds)

    Specifies the time at which the token was issued.

  • user (object)

    References the user to which the token belongs.

    Includes the full resource description of a user.

  • methods (list)

    The methods attribute indicates the accumulated set of authentication methods used to obtain the token. For example, if the token was obtained by password authentication, it will contain password. Later, if the token is exchanged using the tokenauthentication method one or more times, the subsequently created tokens will contain both password and token in theirmethods attribute.

    Notice the difference between methods and multifactor authentication. The methods attribute merely indicates the methods used to authenticate the user for the given token. It is up to the client to look for specific methods to determine the total number of factors.

Optional attributes:

  • project (object)

    Specifies a project authorization scope of the token. If this attribute is not provided, then the token is not authorized to access any projects. This attribute must not be included if a domain attribute is included.

    Includes the full resource description of a project.

  • domain (object)

    Specifies a domain authorization scope of the token. This is to provide authorization appropriate to domain-wide APIs, for example user and group management within a domain. Domain authorization does not grant authorization to projects within the domain. If this attribute is not provided, then the token is not authorized to access any domain level resources. This attribute must not be included if a project attribute is included.

    Includes the full resource description of a domain.

  • catalog (object)

    Specifies all endpoints available to/for the token.

    FIXME(dolph): revise with specific expectations.

  • bind (object) New in version 3.1

    Token binding refers to the practice of embedding information from external authentication providers (like a company's Kerberos server) inside the token such that a client may validate that the token is used in conjunction with that authentication mechanism. By coupling this authentication we can prevent re-use of a stolen token as an attacker would not have access to the external authentication.

    Specifies one or more external authorization mechanisms that can be used in conjunction with the token for it to be validated by a bind enforcing client. For example a token may only be used over a Kerberos authenticated connection or with a specific client certificate.

    Includes one or more mechanism identifiers with protocol specific data. The officially supported mechanisms are kerberosand x509 where:

    • The kerberos bind payload is of the form:

      "kerberos": {
          "principal": "USER@REALM"
      }
      

      where the user's Kerberos principal is "USER@REALM".

    • The x509 bind payload is of the form:

      "x509": {
          "fingerprint": "0123456789ABCDEF",
          "algorithm": "sha1"
      }
      

      the fingerprint is the hash of the client certificate to be validated in the specified algorithm. It should be the hex form without seperating spaces or colons. The only supported algorithm is currently sha1.

Example entity:

{
    "token": {
        "expires_at": "2013-02-27T18:30:59.999999Z",
        "issued_at": "2013-02-27T16:30:59.999999Z",
        "methods": [
            "password"
        ],
        "bind": {
            "kerberos": {
                "principal": "USER@REALM"
            }
        },
        "user": {
            "domain": {
                "id": "1789d1",
                "links": {
                    "self": "http://identity:35357/v3/domains/1789d1"
                },
                "name": "example.com"
            }
            "email": "joe@example.com",
            "id": "0ca8f6",
            "links": {
                "self": "http://identity:35357/v3/users/0ca8f6"
            },
            "name": "Joe"
        }
    }
}

Policy

Policies represent arbitrarily serialized policy engine rule sets to be consumed by remote services.

Additional required attributes:

  • blob (string)

    The policy rule set itself, as a serialized blob.

  • type (string)

    The MIME Media Type of the serialized policy blob.

Example entity:

{
    "policy": {
        "blob": "{\"default\": false}",
        "id": "c41a4c",
        "links": {
            "self": "http://identity:35357/v3/policies/c41a4c"
        },
        "type": "application/json"
    }
}

Extensions /v3/extensions

New in version 3.2

Extensions provide a way of adding API functionality to keystone that is not part of the core API. This API provides a way of discovering what extensions are available on a server and the url entry point for each extension.

Additional required attributes:

  • url

    The fully qualified url with which to communicate with the extension.

Optional attributes:

  • name (string)

    A human readable name for the extension.

  • description (string)

    A human readable description of the extension.

  • version (string)

    A string indicating the version of the extension API that is provided.

  • extra (object)

    Additional data that the extension wants to advertise. This information will change between extensions and there is no set format to adhere to. It is assumed that clients who know how to use this extension will also understand the format of extradata. If there is no extra data it is valid for the object to be empty or omitted.

Example entity:

{
    "extension": {
        "id": "OS-MYEXT",
        "name": "My Extension",
        "description": "An Example Extension",
        "version": "v1.0",
        "url": "http://identity:35357/OS-MYEXT"
        "links": {
            "self": "http://identity:35357/v3/extensions/OS-MYEXT",
        }
        "extra": {
            ...
        }
    }
}

Core API

Versions

List versions; GET /

(TBD: This needs additional definition to match the detail below)

Tokens

Use cases:

  • Given a user name & password, get a token to represent the user.
  • Given a token, get a list of other domain/projects the user can access.
  • Given a token, validate the token and return user, domain, project, roles and potential endpoints.
  • Given a valid token, request another token with a different domain/project (change domain/project being represented with the user).
  • Given a valid token, force it's immediate revocation.
Authenticate: POST /auth/tokens

Each request to create a token contains an attribute with identiy information and, optionally, a scope describing the authorization scope being requested. Example request structure:

{
    "auth": {
        "identity": { ... },
        "scope": { ... }
    }
}
Authentication: authentication

Authentication is performed by specifying a list of authentication methods, each with a corresponding object, containing any attributes required by the authentication method. Example request structure for three arbitrary authentication methods:

{
    "auth": {
        "identity": {
            "methods": ["x", "y", "z"],
            "x": { ... },
            "y": { ... },
            "z": { ... }
        }
    }
}
The password authentication method

To authenticate by password, the user must be uniquely identified in addition to providing a password attribute.

The user may be identified by either id or name. A user's id is sufficient to uniquely identify the user. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "id": "0ca8f6",
                    "password": "secrete"
                }
            }
        }
    }
}

If the user is specified by name, then the domain of the user must also be specified in order to uniquely identify the user. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "domain": {
                        "id": "1789d1"
                    },
                    "name": "Joe",
                    "password": "secrete"
                }
            }
        }
    }
}

Alternatively, a domain name may be used to uniquely identify the user. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "domain": {
                        "name": "example.com"
                    },
                    "name": "Joe",
                    "password": "secrete"
                }
            }
        }
    }
}
The token authentication method

If the authenticating user is already in possession of a valid token, then that token is sufficient to identity the user. This method is typically used in combination with request to change authorization scope.

{
    "auth": {
        "identity": {
            "methods": [
                "token"
            ],
            "token": {
                "id": "e80b74"
            }
        }
    }
}
Scope: scope

An authorization scope, including either a project or domain, can be optionally specified as part of the request. If both a domain and a project are specified, an HTTP 400 Bad Request will be returned, as a token cannot be simultaneously scoped to both a project and domain.

project may be specified by either id or name. An id is sufficient to uniquely identify a project. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "id": "0ca8f6",
                    "password": "secrete"
                }
            }
        },
        "scope": {
            "project": {
                "id": "263fd9"
            }
        }
    }
}

If a project is specified by name, then the domain of the project must also be specified in order to uniquely identify theproject. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "id": "0ca8f6",
                    "password": "secrete"
                }
            }
        },
        "scope": {
            "project": {
                "domain": {
                    "id": "1789d1"
                },
                "name": "project-x"
            }
        }
    }
}

Alternatively, a domain name may be used to uniquely identify the project. Example request:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "id": "0ca8f6",
                    "password": "secrete"
                }
            }
        },
        "scope": {
            "project": {
                "domain": {
                    "name": "example.com"
                },
                "name": "project-x"
            }
        }
    }
}

If neither a project nor a domain is provided for scope, and the authenticating user has a defined default project (the user'sdefault_project_id attribute), then this will be treated as the preferred authorization scope. If there is no default project defined, then a token will be issued without an explicit scope of authorization.

New in version 3.1 Additionally, if the user's default project is invalid, a token will be issued without an explicit scope of authorization.

Catalog Opt-Out: 'POST /v3/auth/tokens?nocatalog'

New in version 3.1 If the caller specifies a nocatalog query parameter in the authentication request, then the authentication response will not contain the service catalog. The service catalog will otherwise be included in the response by default.

Authentication responses

A response without an explicit authorization scope does not contain a catalogprojectdomain or roles but can be used to uniquely identify the user. Example response:

Headers:
    X-Subject-Token: e80b74

{
    "token": {
        "expires_at": "2013-02-27T18:30:59.999999Z",
        "issued_at": "2013-02-27T16:30:59.999999Z",
        "methods": [
            "password"
        ],
        "user": {
            "domain": {
                "id": "1789d1",
                "links": {
                    "self": "http://identity:35357/v3/domains/1789d1"
                },
                "name": "example.com"
            },
            "id": "0ca8f6",
            "links": {
                "self": "http://identity:35357/v3/users/0ca8f6"
            },
            "name": "Joe"
        }
    }
}

Notice that token ID is not part of the token data. Rather, it is conveyed in the X-Subject-Token header.

A token scoped to a project will also have a service catalog, along with the user's roles applicable to the project. Example response:

Headers: X-Subject-Token

X-Subject-Token: e80b74

{
    "token": {
        "catalog": "FIXME(dolph): need an example here",
        "expires_at": "2013-02-27T18:30:59.999999Z",
        "issued_at": "2013-02-27T16:30:59.999999Z",
        "methods": [
            "password"
        ],
        "project": {
            "domain": {
                "id": "1789d1",
                "links": {
                    "self": "http://identity:35357/v3/domains/1789d1"
                },
                "name": "example.com"
            },
            "id": "263fd9",
            "links": {
                "self": "http://identity:35357/v3/projects/263fd9"
            },
            "name": "project-x"
        },
        "roles": [
            {
                "id": "76e72a",
                "links": {
                    "self": "http://identity:35357/v3/roles/76e72a"
                },
                "name": "admin"
            },
            {
                "id": "f4f392",
                "links": {
                    "self": "http://identity:35357/v3/roles/f4f392"
                },
                "name": "member"
            }
        ],
        "user": {
            "domain": {
                "id": "1789d1",
                "links": {
                    "self": "http://identity:35357/v3/domains/1789d1"
                },
                "name": "example.com"
            },
            "id": "0ca8f6",
            "links": {
                "self": "http://identity:35357/v3/users/0ca8f6"
            },
            "name": "Joe"
        }
    }
}

A token scoped to a domain will also have a service catalog along with the user's roles applicable to the domain. Example response:

Headers: X-Subject-Token

X-Subject-Token: e80b74

{
    "token": {
        "catalog": "FIXME(dolph): need an example here",
        "expires_at": "2013-02-27T18:30:59.999999Z",
        "issued_at": "2013-02-27T16:30:59.999999Z",
        "methods": [
            "password"
        ],
        "domain": {
            "id": "1789d1",
            "links": {
                "self": "http://identity:35357/v3/domains/1789d1"
            },
            "name": "example.com"
        },
        "roles": [
            {
                "id": "76e72a",
                "links": {
                    "self": "http://identity:35357/v3/roles/76e72a"
                },
                "name": "admin"
            },
            {
                "id": "f4f392",
                "links": {
                    "self": "http://identity:35357/v3/roles/f4f392"
                },
                "name": "member"
            }
        ],
        "user": {
            "domain": {
                "id": "1789d1",
                "links": {
                    "self": "http://identity:35357/v3/domains/1789d1"
                },
                "name": "example.com"
            },
            "id": "0ca8f6",
            "links": {
                "self": "http://identity:35357/v3/users/0ca8f6"
            },
            "name": "Joe"
        }
    }
}
Authentication failures

Several authentication errors are possible, including 403 Forbidden and 409 Conflict, but here's an example of an HTTP 401 Unauthorized response:

Status: 401 Not Authorized

{
    "error": {
        "code": 401,
        "message": "The request you have made requires authentication",
        "title": "Not Authorized"
    }
}

Optionally, the Identity service implementation may return an authentication attribute to indicate the supported authentication methods.

Status: 401 Not Authorized

{
    "error": {
        "code": 401,
        "identity": {
            "methods": [
                "password",
                "token",
                "challenge-response"
            ]
        },
        "message": "Need to authenticate with one or more supported methods",
        "title": "Not Authorized"
    }
}

For authentication processes which require multiple round trips, the Identity service implementation may return an HTTP 401 Not Authorized with additional information for the next authentication step.

For example:

Status: 401 Not Authorized

{
    "error": {
        "code": 401,
        "identity": {
            "challenge-response": {
                "challenge": "What was the zip code of your birth place?",
                "session_id": "123456"
            },
            "methods": [
                "challenge-response"
            ]
        },
        "message": "Additional authentications steps required.",
        "title": "Not Authorized"
    }
}
Validate token: GET /auth/tokens

To validate a token using the Identity API, pass your own token in the X-Auth-Token header, and the token to be validated in theX-Subject-Token header. Example request:

Headers:
    X-Auth-Token: 1dd7e3
    X-Subject-Token: c67580

No request body is required.

The Identity service will return the exact same response as when the subject token was issued by POST /auth/tokens.

Check token: HEAD /auth/tokens

This call is identical to GET /auth/tokens, but no response body is provided, even if an error occurs or the token is invalid. A 204 response indicates that the X-Subject-Token is valid.

Revoke token: DELETE /auth/tokens

This call is identical to HEAD /auth/tokens except that the X-Subject-Token token is immediately invalidated, regardless of it'sexpires_at attribute. An additional X-Auth-Token is not required.

Catalog

The key use cases we need to cover:

  • CRUD for services and endpoints
  • Retrieving an endpoint URL by service, region, and interface
List services: GET /services

query filter for "type" (optional)

Response:

Status: 200 OK

{
    "services": [
        {
            "id": "--service-id--",
            "links": {
                "self": "http://identity:35357/v3/services/--service-id--"
            },
            "name": "--service-name",
            "type": "volume"
        },
        {
            "id": "--service-id--",
            "links": {
                "self": "http://identity:35357/v3/services/--service-id--"
            },
            "name": "--service-name",
            "type": "identity"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/services",
        "previous": null,
        "next": null
    }
}
Get service: GET /services/{service_id}

Response:

Status: 200 OK

{
    "service": {
        "id": "--service-id--",
        "links": {
            "self": "http://identity:35357/v3/services/--service-id--"
        },
        "name": "--service-name",
        "type": "volume"
    }
}
Create service: POST /services

Request:

{
    "service": {
        "name": "--optional--",
        "type": "..."
    }
}

Response:

Status: 201 Created

{
    "service": {
        "id": "--service-id--",
        "links": {
            "self": "http://identity:35357/v3/services/--service-id--"
        },
        "name": "--service-name--",
        "type": "volume"
    }
}
Update service: PATCH /services/{service_id}

The request block is the same as the one for create service, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "service": {
        "id": "--service-id--",
        "links": {
            "self": "http://identity:35357/v3/services/--service-id--"
        },
        "name": "--service-name",
        "type": "volume"
    }
}
Delete service: DELETE /services/{service_id}
  • Note: deleting a service when endpoints exist should either 1) delete all associated endpoints or 2) fail until endpoints are deleted

Response:

Status: 204 No Content

Endpoints

List endpoints: GET /endpoints

query filter for "interface" and "service_id" (optional)

Response:

Status: 200 OK

{
    "endpoints": [
        {
            "id": "--endpoint-id--",
            "interface": "public",
            "links": {
                "self": "http://identity:35357/v3/endpoints/--endpoint-id--"
            },
            "name": "the public volume endpoint",
            "region": "--region--",
            "service_id": "--service-id--"
        },
        {
            "id": "--endpoint-id--",
            "interface": "internal",
            "links": {
                "self": "http://identity:35357/v3/endpoints/--endpoint-id--"
            },
            "name": "the internal volume endpoint",
            "region": "--region--",
            "service_id": "--service-id--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/endpoints",
        "previous": null,
        "next": null
    }
}
Create endpoint: POST /endpoints

Request:

{
    "endpoint": {
        "interface": "[admin|public|internal]",
        "name": "name",
        "region": "--optional--",
        "url": "..."
    }
}

Response:

Status: 201 Created

{
    "endpoint": {
        "id": "--endpoint-id--",
        "interface": "internal",
        "links": {
            "self": "http://identity:35357/v3/endpoints/--endpoint-id--"
        },
        "name": "the internal volume endpoint",
        "region": "--region--",
        "service_id": "--service-id--"
    }
}
Update endpoint: PATCH /endpoints/{endpoint_id}

The request block is the same as the one for create endpoint, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "endpoint": {
        "id": "--endpoint-id--",
        "interface": "internal",
        "links": {
            "self": "http://identity:35357/v3/endpoints/--endpoint-id--"
        },
        "name": "the internal volume endpoint",
        "region": "--region--",
        "service_id": "--service-id--"
    }
}
Delete endpoint: DELETE /endpoints/{endpoint_id}

Response:

Status: 204 No Content

Identity

The key use cases we need to cover:

  • CRUD on a user
  • associating a user with a project
  • CRUD on a domain
  • CRUD on a project

Domains

Create domain: POST /domains

Request:

{
    "domain": {
        "description": "--optional--",
        "enabled": true,
        "name": "..."
    }
}

Response:

Status: 201 Created

{
    "domain": {
        "description": "desc of domain",
        "enabled": true,
        "id": "--domain-id--",
        "links": {
            "self": "http://identity:35357/v3/domains/--domain-id--"
        },
        "name": "my domain"
    }
}
List domains: GET /domains

query filter for "name" and "enabled" (optional)

Response:

Status: 200 OK

{
    "domains": [
        {
            "description": "desc of domain",
            "enabled": true,
            "id": "--domain-id--",
            "links": {
                "self": "http://identity:35357/v3/domains/--domain-id--"
            },
            "name": "my domain"
        },
        {
            "description": "desc of another domain",
            "enabled": true,
            "id": "--domain-id--",
            "links": {
                "self": "http://identity:35357/v3/domains/--domain-id--"
            },
            "name": "another domain"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/domains",
        "previous": null,
        "next": null
    }
}
Get domain: GET /domains/{domain_id}

Response:

Status: 200 OK

{
    "domain": {
        "description": "desc of domain",
        "enabled": true,
        "id": "--domain-id--",
        "links": {
            "self": "http://identity:35357/v3/domains/--domain-id--"
        },
        "name": "my domain"
    }
}
Update domain: PATCH /domains/{domain_id}

The request block is the same as the one for create domain, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "domain": {
        "description": "desc of domain",
        "enabled": true,
        "id": "--domain-id--",
        "links": {
            "self": "http://identity:35357/v3/domains/--domain-id--"
        },
        "name": "my domain"
    }
}
Delete domain: DELETE /domains/{domain_id}

Deleting a domain will delete all the entities owned by it (Users, Groups & Projects), as well as any Credentials and Role grants that relate to these entities.

In order to minimize the risk of an inadvertent deletion of a domain and its entities, a domain must first be disabled (using the update domain API) before a successful delete domain API call can be made. Attempting to delete an enabled domain will result in an HTTP 403 Forbidden response.

Response:

Status: 204 No Content

Projects

Create project: POST /projects

Request:

{
    "project": {
        "description": "...",
        "domain_id": "...",
        "enabled": true,
        "name": "..."
    }
}

Response:

Status: 201 Created

{
    "project": {
        "domain_id": "--domain-id--",
        "enabled": true,
        "id": "--project-id--",
        "links": {
            "self": "http://identity:35357/v3/projects/--project-id--"
        },
        "name": "a project name"
    }
}
List projects: GET /projects

query filter for "domain_id", "enabled", "name" (optional)

Response:

Status: 200 OK

{
    "projects": [
        {
            "domain_id": "--domain-id--",
            "enabled": true,
            "id": "--project-id--",
            "links": {
                "self": "http://identity:35357/v3/projects/--project-id--"
            },
            "name": "a project name"
        },
        {
            "domain_id": "--domain-id--",
            "enabled": true,
            "id": "--project-id--",
            "links": {
                "self": "http://identity:35357/v3/projects/--project-id--"
            },
            "name": "another project"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/projects",
        "previous": null,
        "next": null
    }
}
Get project: GET /projects/{project_id}

Response:

Status: 200 OK

{
    "project": {
        "domain_id": "--domain-id--",
        "enabled": true,
        "id": "--project-id--",
        "links": {
            "self": "http://identity:35357/v3/projects/--project-id--"
        },
        "name": "a project name"
    }
}
Update project: PATCH /projects/{project_id}

The request block is the same as the one for create project, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "project": {
        "domain_id": "--domain-id--",
        "enabled": true,
        "id": "--project-id--",
        "links": {
            "self": "http://identity:35357/v3/projects/--project-id--"
        },
        "name": "a project name"
    }
}
Delete project: DELETE /projects/{project_id}
Status: 204 No Content

Users

Create user: POST /users

Request:

{
    "user": {
        "default_project_id": "...",
        "description": "...",
        "domain_id": "--optional--",
        "email": "...",
        "enabled": true,
        "name": "...",
        "password": "--optional--"
    }
}

Response:

Status: 201 Created

{
    "user": {
        "default_project_id": "--default-project-id--",
        "description": "a user",
        "domain_id": "1789d1",
        "email": "...",
        "enabled": true,
        "id": "--user-id--",
        "links": {
            "self": "http://identity:35357/v3/users/--user-id--"
        },
        "name": "admin"
    }
}
List users: GET /users

query filter for "domain_id", "email", "enabled", "name" (optional)

Response:

Status: 200 OK

{
    "users": [
        {
            "default_project_id": "--default-project-id--",
            "description": "a user",
            "domain_id": "1789d1",
            "email": "...",
            "enabled": true,
            "id": "--user-id--",
            "links": {
                "self": "http://identity:35357/v3/users/--user-id--"
            },
            "name": "admin"
        },
        {
            "default_project_id": "--default-project-id--",
            "description": "another user",
            "domain_id": "1789d1",
            "email": "...",
            "enabled": true,
            "id": "--user-id--",
            "links": {
                "self": "http://identity:35357/v3/users/--user-id--"
            },
            "name": "someone"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/users",
        "previous": null,
        "next": null
    }
}
Get user: GET /users/{user_id}

Response:

Status: 200 OK

{
    "user": {
        "default_project_id": "--default-project-id--",
        "description": "a user",
        "domain_id": "1789d1",
        "email": "...",
        "enabled": true,
        "id": "--user-id--",
        "links": {
            "self": "http://identity:35357/v3/users/--user-id--"
        },
        "name": "admin"
    }
}
List user projects: GET /users/{user_id}/projects

query filter for "name", "enabled" on project resources (optional)

Response:

Status: 200 OK

{
    "projects": [
        {
            "domain_id": "--domain-id--",
            "enabled": true,
            "id": "--project-id--",
            "links": {
                "self": "http://identity:35357/v3/projects/--project-id--"
            },
            "name": "a project name"
        },
        {
            "domain_id": "--domain-id--",
            "enabled": true,
            "id": "--project-id--",
            "links": {
                "self": "http://identity:35357/v3/projects/--project-id--"
            },
            "name": "another domain"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/users/--user-id--/projects",
        "previous": null,
        "next": null
    }
}
List groups of which a user is a member: GET /users/{user_id}/groups

query filter for "name" (optional)

Response:

Status: 200 OK

{
    "groups": [
        {
            "description": "Developers cleared for work on all general projects"
            "domain_id": "--domain-id--",
            "id": "--group-id--",
            "links": {
                "self": "http://identity:35357/v3/groups/--group-id--"
            },
            "name": "Developers"
        },
        {
            "description": "Developers cleared for work on secret projects"
            "domain_id": "--domain-id--",
            "id": "--group-id--",
            "links": {
                "self": "http://identity:35357/v3/groups/--group-id--"
            },
            "name": "Secure Developers"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/users/--user-id--/groups",
        "previous": null,
        "next": null
    }
}
Update user: PATCH /users/{user_id}

The request block is the same as the one for create user, except that only the attributes that are being updated need to be included. Use this method attempt to update user password or enable/disable the user. This may return a HTTP 501 Not Implemented if the back-end driver doesn't allow for the functionality.

Response:

Status: 200 OK

{
    "user": {
        "default_project_id": "--default-project-id--",
        "description": "a user",
        "domain_id": "1789d1",
        "email": "...",
        "enabled": true,
        "id": "--user-id--",
        "links": {
            "self": "http://identity:35357/v3/users/--user-id--"
        },
        "name": "admin"
    }
}
Delete user: DELETE /users/{user_id}

Response:

Status: 204 No Content

Groups

Create group: POST /groups

Request:

{
    "group": {
        "description": "--optional--",
        "domain_id": "--optional--",
        "name": "..."
    }
}

Response:

Status: 201 Created

{
    "group": {
        "description": "Developers cleared for work on secret projects",
        "id": "--group-id--",
        "links": {
            "self": "http://identity:35357/v3/groups/--group-id--"
        },
        "name": "Secure Developers"
    }
}
List groups: GET /groups

query filter for "domain_id", "name" (optional)

Response:

Status: 200 OK

{
    "groups": [
        {
            "description": "Developers cleared for work on all general projects"
            "domain_id": "--domain-id--",
            "id": "--group-id--",
            "links": {
                "self": "http://identity:35357/v3/groups/--group-id--"
            },
            "name": "Developers"
        },
        {
            "description": "Developers cleared for work on secret projects"
            "domain_id": "--domain-id--",
            "id": "--group-id--",
            "links": {
                "self": "http://identity:35357/v3/groups/--group-id--"
            },
            "name": "Secure Developers"
        },
        {
            "description": "Testers cleared for work on all general projects"
            "domain_id": "--domain-id--",
            "id": "--group-id--",
            "links": {
                "self": "http://identity:35357/v3/groups/--group-id--"
            },
            "name": "Testers"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/groups",
        "previous": null,
        "next": null
    }
}
Get group: GET /groups/{group_id}

Response:

Status: 200 OK

{
    "group": {
        "description": "Developers cleared for work on secret projects",
        "id": "--group-id--",
        "links": {
            "self": "http://identity:35357/v3/groups/--group-id--"
        },
        "name": "Secure Developers"
    }
}
List users who are members of a group: GET /groups/{group_id}/users

query filter for "name", "enabled", "email" (optional)

Response:

Status: 200 OK

{
    "users": [
        {
            "default_project_id": "--default-project-id--",
            "description": "a user",
            "domain_id": "--domain-id--",
            "email": "...",
            "enabled": true,
            "id": "--user-id--",
            "links": {
                "self": "http://identity:35357/v3/users/--user-id--"
            },
            "name": "admin"
        },
        {
            "default_project_id": "--default-project-id--",
            "description": "another user",
            "domain_id": "--domain-id--",
            "email": "...",
            "enabled": true,
            "id": "--user-id--",
            "links": {
                "self": "http://identity:35357/v3/users/--user-id--"
            },
            "name": "someone"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/groups/--group-id--/users",
        "previous": null,
        "next": null
    }
}
Update group: PATCH /groups/{group_id}

The request block is the same as the one for create group, except that only the attributes that are being updated need to be included. This may return a HTTP 501 Not Implemented if the back-end driver doesn't allow for the functionality.

Response:

Status: 200 OK

{
    "group": {
        "description": "Developers cleared for work on secret projects",
        "id": "--group-id--",
        "links": {
            "self": "http://identity:35357/v3/groups/--group-id--"
        },
        "name": "Secure Developers"
    }
}
Delete group: DELETE /groups/{group_id}

Response:

Status: 204 No Content
Add user to group: PUT /groups/{group_id}/users/{user_id}

Response:

Status: 204 No Content
Remove user from group: DELETE /groups/{group_id}/users/{user_id}

Response:

Status: 204 No Content
Check if user is member of group: HEAD /groups/{group_id}/users/{user_id}

Response:

Status: 204 No Content

Credentials

The key use cases we need to cover:

  • CRUD on a credential
Create credential: POST /credentials

This example shows creating an EC2 style credential where the credentials are a combination of access_key and secret. Other credentials (such as access_key) may be supported by simply changing the content of the key data.

Request:

{
    "credential": {
        "blob": "--JSON serialized object containing 'access' and 'secret'--",
        "project_id": "--optional--",
        "type": "ec2",
        "user_id": "--user--id--"
    }
}

Response:

Status: 201 Created

{
    "credential": {
        "blob": "--JSON serialized object containing 'access' and 'secret'--",
        "id": "--credential-id--",
        "links": {
            "self": "http://identity:35357/v3/credentials/--credential-id--"
        },
        "project_id": "--project-id--",
        "type": "ec2",
        "user_id": "--user--id--"
    }
}
List credentials: GET /credentials

Response:

Status: 200 OK

{
    "credentials": [
        {
            "blob": "--JSON serialized object containing 'access' and 'secret'--",
            "id": "--credential-id--",
            "links": {
                "self": "http://identity:35357/v3/credentials/--credential-id--"
            },
            "project_id": "--project-id--",
            "type": "ec2",
            "user_id": "--user--id--"
        },
        {
            "blob": "--JSON serialized object containing 'access' and 'secret'--",
            "id": "--credential-id--",
            "links": {
                "self": "http://identity:35357/v3/credentials/--credential-id--"
            },
            "project_id": "--project-id--",
            "type": "ec2",
            "user_id": "--user--id--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/credentials",
        "previous": null,
        "next": null
    }
}
Get credential: GET /credentials/{credential_id}

Response:

Status: 200 OK

{
    "credential": {
        "blob": "--JSON serialized object containing 'access' and 'secret'--",
        "id": "--credential-id--",
        "links": {
            "self": "http://identity:35357/v3/credentials/--credential-id--"
        },
        "project_id": "--project-id--",
        "type": "ec2",
        "user_id": "--user--id--"
    }
}
Update credential: PATCH /credentials/{credential_id}

The request block is the same as the one for create credential, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "credential": {
        "blob": "--JSON serialized object containing 'access' and 'secret'--",
        "id": "--credential-id--",
        "links": {
            "self": "http://identity:35357/v3/credentials/--credential-id--"
        },
        "project_id": "--project-id--",
        "type": "ec2",
        "user_id": "--user--id--"
    }
}
Delete credential: DELETE /credentials/{credential_id}

Response:

Status: 204 No Content

Roles

The key use cases we need to cover:

  • CRUD on a role
  • Associating a role with a project or domain
Create role: POST /roles

Request:

{
    "role": {
        "name": "..."
    }
}

Response:

Status: 201 Created

{
    "role": {
        "id": "--role-id--",
        "links": {
            "self": "http://identity:35357/v3/roles/--role-id--"
        },
        "name": "a role name"
    }
}
List roles: GET /roles

query filter for "name" (optional)

Response:

Status: 200 OK

{
    "roles": [
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "a role name"
        },
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "a role name"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/roles",
        "previous": null,
        "next": null
    }
}
Get role: GET /roles/{role_id}

Response:

Status: 200 OK

{
    "role": {
        "id": "--role-id--",
        "links": {
            "self": "http://identity:35357/v3/roles/--role-id--"
        },
        "name": "a role name"
    }
}
Update role: PATCH /roles/{role_id}

The request block is the same as the one for create role, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "role": {
        "id": "--role-id--",
        "links": {
            "self": "http://identity:35357/v3/roles/--role-id--"
        },
        "name": "a role name"
    }
}
Delete role: DELETE /roles/{role_id}

Response:

Status: 204 No Content
Grant role to user on domain: PUT /domains/{domain_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Grant role to group on domain:PUT /domains/{domain_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
List user's roles on domain: GET /domains/{domain_id}/users/{user_id}/roles

Response:

Status: 200 OK

{
    "roles": [
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--",
        },
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/domains/--domain_id--/users/--user_id--/roles",
        "previous": null,
        "next": null
    }
}
List group's roles on domain: GET /domains/{domain_id}/groups/{group_id}/roles

Response:

Status: 200 OK

{
    "roles": [
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--",
        },
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/domains/--domain_id--/groups/--group_id--/roles",
        "previous": null,
        "next": null
    }
}
Check if user has role on domain:HEAD /domains/{domain_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Check if group has role on domain:HEAD /domains/{domain_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
Revoke role from user on domain:DELETE /domains/{domain_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Revoke role from group on domain:DELETE /domains/{domain_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
Grant role to user on project:PUT /projects/{project_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Grant role to group on project:PUT /projects/{project_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
List user's roles on project: GET /projects/{project_id}/users/{user_id}/roles

Response:

Status: 200 OK

{
    "roles": [
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--",
        },
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/projects/--project_id--/users/--user_id--/roles",
        "previous": null,
        "next": null
    }
}
List group's roles on project: GET /projects/{project_id}/groups/{group_id}/roles

Response:

Status: 200 OK

{
    "roles": [
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--",
        },
        {
            "id": "--role-id--",
            "links": {
                "self": "http://identity:35357/v3/roles/--role-id--"
            },
            "name": "--role-name--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/projects/--project_id--/groups/--group_id--/roles",
        "previous": null,
        "next": null
    }
}
Check if user has role on project:HEAD /projects/{project_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Check if group has role on project:HEAD /projects/{project_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
Revoke role from user on project:DELETE /projects/{project_id}/users/{user_id}/roles/{role_id}

Response:

Status: 204 No Content
Revoke role from group on project:DELETE /projects/{project_id}/groups/{group_id}/roles/{role_id}

Response:

Status: 204 No Content
List effective role assignments: GET /role_assignments

query_filter: group.id, role.id, scope.domain.id, scope.project.id, user.id (all optional)
query_string: effective (optional, default false)

Get a list of role assignments. This API is only available from v3.1 onwards.

If no query filters are specified, then this API will return a list of all role assignments.

Response:

Status: 200 OK

{
    "role_assignments": [
        {
            "links": {
                "assignment": "http://identity:35357/v3/domains/--domain-id--/users/--user-id--/roles/--role-id--"
            },
            "role": {
                "id": "--role-id--"
            },
            "scope": {
                "domain": {
                    "id": "--domain-id--"
                }
            },
            "user": {
                "id": "--user-id--"
            }
        },
        {
            "group": {
                "id": "--group-id--"
            },
            "links": {
                "assignment": "http://identity:35357/v3/projects/--project-id--/groups/--group-id--/roles/--role-id--"
            },
            "role": {
                "id": "--role-id--"
            },
            "scope": {
                "project": {
                    "id": "--project-id--"
                }
            }
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/role_assignments",
        "previous": null,
        "next": null
    }
}

Since this list is likely to be very long, this API would typically always be used with one of more of the filter queries. Some typical examples are:

GET /role_assignments?user.id={user_id} would list all role assignments involving the specified user.

GET /role_assignments?scope.project.id={project_id} would list all role assignments involving the specified project.

Each role assignment entity in the collection contains a link to the assignment that gave rise to this entity.

If the query_string effective is specified then, rather than simply returning a list of role assignments that have been made, the API returns a list of effective assignments at the user, project and domain level, having allowed for the effects of group membership. Since the effects of group membership have already been allowed for, the group role assignment entities themselves will not be returned in the collection. This represents the effective role assignments that would be included in a scoped token. The same set of query filters can also be used with the effective query string. For example:

GET /role_assignments?user.id={user_id}&effective would, in other words, answer the question "what can this user actually do?".

GET /role_assignments?user.id={user_id}&scope.project.id={project_id}&effective would return the equivalent set of role assignments that would be included in the token response of a project scoped token.

An example response for an API call with the query_string effective specified is given below:

Response:

Status: 200 OK

{
    "role_assignments": [
        {
            "links": {
                "assignment": "http://identity:35357/v3/domains/--domain-id--/users/--user-id--/roles/--role-id--"
            },
            "role": {
                "id": "--role-id--"
            },
            "scope": {
                "domain": {
                    "id": "--domain-id--"
                }
            },
            "user": {
                "id": "--user-id--"
            }
        },
        {
            "links": {
                "assignment": "http://identity:35357/v3/projects/--project-id--/groups/--group-id--/roles/--role-id--",
                "membership": "http://identity:35357/v3/groups/--group-id--/users/--user-id--"
            },
            "role": {
                "id": "--role-id--"
            },
            "scope": {
                "project": {
                    "id": "--project-id--"
                }
            },
            "user": {
                "id": "--user-id--"
            }
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/role_assignments?effective",
        "previous": null,
        "next": null
    }
}

The entity links section of a response using the effective query string also contains, for entities that are included by virtue of group memebership, a url that can be used to access the membership of the group.

Policies

The key use cases we need to cover:

  • CRUD on a policy
Create policy: POST /policies

Request:

{
    "blob": "--serialized-blob--",
    "type": "--serialization-mime-type--"
}

Response:

Status: 201 Created

{
    "policy": {
        "blob": "--serialized-blob--",
        "id": "--policy-id--",
        "links": {
            "self": "http://identity:35357/v3/policies/--policy-id--"
        },
        "type": "--serialization-mime-type--"
    }
}
List policies: GET /policies

query filter for "type" (optional)

Response:

Status: 200 OK

{
    "policies": [
        {
            "blob": "--serialized-blob--",
            "id": "--policy-id--",
            "links": {
                "self": "http://identity:35357/v3/policies/--policy-id--"
            },
            "type": "--serialization-mime-type--"
        },
        {
            "blob": "--serialized-blob--",
            "id": "--policy-id--",
            "links": {
                "self": "http://identity:35357/v3/policies/--policy-id--"
            },
            "type": "--serialization-mime-type--"
        }
    ],
    "links": {
        "self": "http://identity:35357/v3/policies",
        "previous": null,
        "next": null
    }
}
Get policy: GET /policies/{policy_id}

Response:

Status: 200 OK

{
    "policy": {
        "blob": "--serialized-blob--",
        "id": "--policy-id--",
        "links": {
            "self": "http://identity:35357/v3/policies/--policy-id--"
        },
        "type": "--serialization-mime-type--"
    }
}
Update policy: PATCH /policies/{policy_id}

The request block is the same as the one for create policy, except that only the attributes that are being updated need to be included.

Response:

Status: 200 OK

{
    "policy": {
        "blob": "--serialized-blob--",
        "id": "--policy-id--",
        "links": {
            "self": "http://identity:35357/v3/policies/--policy-id--"
        },
        "type": "--serialization-mime-type--"
    }
}
Delete policy: DELETE /policies/{policy_id}

Response:

Status: 204 No Content

Extensions

The key use cases we need to cover:

  • CRUD on a extension
List extensions: GET /extensions

Response:

Status: 200 OK

{
    "extensions": [
        {
            "id": "--extension-id--",
            "name": "--extension-name--",
            "url": "http://identity:35357/--extension-root--"
            "links": {
                "self": "http://identity:35357/v3/extensions/--extension-id--",
            },
            "extra": {
                "features": ["feature1", "feature5"],
            }
        },
        {
            "id": "--extension-id--",
            "version": "--extension-version--",
            "url": "http://identity:35357/--extension-root--/--extension-version--"
            "links": {
                "self": "http://identity:35357/v3/extensions/--extension-id--",
            },
        },
    ],
    "links": {
        "self": "http://identity:35357/v3/extensions",
        "previous": null,
        "next": null
    }
}
Get extension: GET /extensions/{extension_id}

Response:

Status: 200 OK

{
    "extension": {
        "id": "--extension-id--",
        "name": "--extension-name--",
        "url": "http://identity:35357/--extension-root--"
        "links": {
            "self": "http://identity:35357/v3/extensions/--extension-id--",
        },
    },
}
<think>嗯,用户遇到了“invalid OpenStack Identity credentials”错误,我需要先弄清楚这个错误的常见原因,然后逐步给出解决方案。首先,这个错误通常和认证有关,所以应该从凭证本身开始检查。 首先,用户可能输入了错误的用户名或密码。这时候应该提醒他们检查是否有拼写错误,区分大小写,或者是否使用了正确的项目名称或域。OpenStack支持多种认证方式,比如密码、token、应用密码等,用户可能选错了认证方式。 接下来,考虑权限问题。用户的账号可能被禁用,或者没有访问特定项目的权限。这时候需要确认账号状态,以及是否有正确的角色分配。另外,项目配额是否用尽也可能导致无法认证,虽然这通常会有不同的错误提示,但也不能完全排除。 然后是环境配置的问题。Keystone的端点URL是否正确?如果配置错误,请求可能被发送到错误的地方。网络连接是否正常,是否有防火墙或代理阻止了认证请求?SSL证书问题也可能导致连接失败,尤其是在自签名证书的情况下,需要验证证书或禁用SSL验证(虽然不推荐)。 另外,服务状态也很关键。Keystone服务本身是否正常运行?如果Keystone宕机,所有认证都会失败。其他依赖服务如数据库、消息队列是否正常?比如,如果MySQL宕机,Keystone也无法工作。 还有,Token的有效期可能过期。OpenStack的Token默认有效时间是一小时,如果用户长时间未操作,可能需要重新获取。此外,时区或时间同步问题也可能导致Token验证失败,需要确保所有节点时间同步。 客户端版本兼容性也可能是个问题。旧版本的客户端可能与新版本的Keystone不兼容,导致认证失败。建议用户升级到最新版本。 在排查步骤方面,应该先让用户检查凭证是否正确,然后测试网络连接,查看日志获取更详细的错误信息,验证服务状态,最后考虑其他因素如时间同步和客户端版本。 最后,需要给出总结,让用户系统地一步步排除问题,从基础到复杂,确保他们能有效解决问题。同时提醒他们查看日志的重要性,因为具体错误信息能更快定位问题。</think>好的,遇到 `invalid OpenStack Identity credentials` 错误时,通常表示 **身份认证凭据无效**。以下是逐步排查和解决问题的思路: --- ### **1. 检查凭证信息** #### **可能原因**: - 用户名、密码或项目名称(Project Name)拼写错误。 - 使用了错误的域(Domain)或认证方式(如密码、token、应用密码等)。 - 密钥(API Key)或 Token 已过期。 #### **解决方法**: - **手动验证**:尝试通过 OpenStack Dashboard (Horizon) 登录,确认凭据是否有效。 - **检查环境变量**:若通过命令行或 API 调用,确保环境变量正确(如 `OS_USERNAME`, `OS_PASSWORD`, `OS_PROJECT_NAME`, `OS_AUTH_URL`)。 ```bash # 示例环境变量配置 export OS_USERNAME="your_username" export OS_PASSWORD="your_password" export OS_PROJECT_NAME="your_project" export OS_AUTH_URL=http://<keystone-host>:5000/v3 ``` --- ### **2. 确认权限和项目状态** #### **可能原因**: - 用户账号被禁用或未分配到项目。 - 项目配额已满或被管理员限制。 #### **解决方法**: - 联系 OpenStack 管理员,确认: - 用户账号状态是否 `enabled`。 - 用户是否属于目标项目并分配了角色(如 `member`, `admin`)。 --- ### **3. 检查认证服务(Keystone配置** #### **可能原因**: - Keystone 服务端点(Endpoint)配置错误。 - 身份认证协议不一致(如 HTTP/HTTPS 混用)。 #### **解决方法**: - 确认 `OS_AUTH_URL` 是否指向正确的 Keystone 端点: ```bash # 示例:检查 Keystone 端点 openstack catalog list ``` - 验证协议一致性:若 Keystone 使用 HTTPS,确保客户端请求也使用 HTTPS。 --- ### **4. 网络和证书问题** #### **可能原因**: - 网络不通导致无法访问 Keystone。 - SSL/TLS 证书验证失败(如自签名证书未信任)。 #### **解决方法**: - **测试连通性**: ```bash curl -v http://<keystone-host>:5000/v3 ``` - **绕过证书验证(临时测试)**: ```bash export OS_CACERT="" # 禁用证书验证(仅限测试环境) ``` --- ### **5. 检查服务日志** 通过 Keystone 日志定位具体错误: - **查看 Keystone 日志**(通常位于 `/var/log/keystone/keystone.log`): ```bash tail -f /var/log/keystone/keystone.log | grep "ERROR" ``` - **常见日志线索**: - `Invalid user credentials`:明确凭证错误。 - `Failed to validate token`:Token 无效或过期。 --- ### **6. 其他可能原因** - **时间同步问题**:确保所有节点时间同步(使用 NTP 服务)。 - **客户端版本兼容性**:更新 OpenStack 客户端到最新版本: ```bash pip install --upgrade python-openstackclient ``` --- ### **总结步骤** 1. **验证凭证** → 2. **检查权限** → 3. **确认网络和端点** → 4. **查看日志** → 5. **联系管理员**。 如果问题仍未解决,请提供以下信息以进一步排查: - 使用的 OpenStack 版本(如 Queens、Victoria)。 - 触发错误的具体操作(如 `openstack server list`)。 - 相关日志片段(隐藏敏感信息)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值