mirror of
https://github.com/ansible/awx.git
synced 2026-03-10 14:09:28 -02:30
prelim update to docs
This commit is contained in:
@@ -51,8 +51,8 @@ class LoggedOAuth2Authentication(OAuth2Authentication):
|
|||||||
user, token = ret
|
user, token = ret
|
||||||
username = user.username if user else '<none>'
|
username = user.username if user else '<none>'
|
||||||
logger.debug(smart_text(
|
logger.debug(smart_text(
|
||||||
u"User {} performed a {} to {} through the API using OAuth token {}".format(
|
u"User {} performed a {} to {} through the API using OAuth token {}.".format(
|
||||||
username, request.method, request.path, user
|
username, request.method, request.path, token.pk
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
setattr(user, 'oauth_scopes', [x for x in token.scope.split() if x])
|
setattr(user, 'oauth_scopes', [x for x in token.scope.split() if x])
|
||||||
|
|||||||
@@ -128,8 +128,6 @@ Strict-Transport-Security: max-age=15768000
|
|||||||
{"access_token": "9epHOqHhnXUcgYK8QanOmUQPSgX92g", "token_type": "Bearer", "expires_in": 36000, "refresh_token": "jMRX6QvzOTf046KHee3TU5mT3nyXsz", "scope": "read"}
|
{"access_token": "9epHOqHhnXUcgYK8QanOmUQPSgX92g", "token_type": "Bearer", "expires_in": 36000, "refresh_token": "jMRX6QvzOTf046KHee3TU5mT3nyXsz", "scope": "read"}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Verify by introspecting the access token:
|
|
||||||
>> Need to fill in Introspection Example in the docs here #TODO: Add Introspection
|
|
||||||
|
|
||||||
## Refresh an existing access token
|
## Refresh an existing access token
|
||||||
Suppose we have an existing access token with refresh token provided:
|
Suppose we have an existing access token with refresh token provided:
|
||||||
@@ -146,7 +144,7 @@ Suppose we have an existing access token with refresh token provided:
|
|||||||
"scope": "read write"
|
"scope": "read write"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
`/api/o/token/` endpoint is used for refreshing access token:
|
The `/api/o/token/` endpoint is used for refreshing access token:
|
||||||
```bash
|
```bash
|
||||||
curl -X POST \
|
curl -X POST \
|
||||||
-d "grant_type=refresh_token&refresh_token=AL0NK9TTpv0qp54dGbC4VUZtsZ9r8z" \
|
-d "grant_type=refresh_token&refresh_token=AL0NK9TTpv0qp54dGbC4VUZtsZ9r8z" \
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from awx.api.views import (
|
|||||||
UserAccessList,
|
UserAccessList,
|
||||||
OAuth2ApplicationList,
|
OAuth2ApplicationList,
|
||||||
OAuth2TokenList,
|
OAuth2TokenList,
|
||||||
OAuth2AuthorizedTokenList,
|
|
||||||
OAuth2PersonalTokenList,
|
OAuth2PersonalTokenList,
|
||||||
UserAuthorizedTokenList,
|
UserAuthorizedTokenList,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -86,3 +86,113 @@ token scope; or POSTing to `/api/applications/<pk>/tokens/` by providing only `s
|
|||||||
the parent application will be automatically linked.
|
the parent application will be automatically linked.
|
||||||
|
|
||||||
# More Docs Coming Soon
|
# More Docs Coming Soon
|
||||||
|
Note a default new application will be created for each new user. So each new user is supposed to see
|
||||||
|
at least one application available to them.
|
||||||
|
|
||||||
|
Tokens, on the other hand, are resources used to actually authenticate incoming requests and mask the
|
||||||
|
permissions of underlying user. Tokens can be created by POSTing to `/api/v2/tokens/`
|
||||||
|
endpoint by providing `application` and `scope` fields to point to related application and specify
|
||||||
|
token scope; or POSTing to `/api/applications/<pk>/tokens/` by providing only `scope`, while
|
||||||
|
the parent application will be automatically linked.
|
||||||
|
|
||||||
|
Individual tokens will be accessible via their primary keys:
|
||||||
|
`/api/<version>/me/oauth/tokens/<primary key of a token>/`. Here is a typical token:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"type": "access_token",
|
||||||
|
"url": "/api/v2/me/oauth/tokens/17/",
|
||||||
|
"related": {
|
||||||
|
"user": "/api/v2/users/1/",
|
||||||
|
"application": "/api/v2/me/oauth/applications/4/",
|
||||||
|
"activity_stream": "/api/v2/me/oauth/tokens/17/activity_stream/"
|
||||||
|
},
|
||||||
|
"summary_fields": {
|
||||||
|
"application": {
|
||||||
|
"id": 4,
|
||||||
|
"name": "admin's token",
|
||||||
|
"client_id": "D6SwhKbfp2LuUjkmiUpMMYFyNqhpv5PTVci7eXTT"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "admin",
|
||||||
|
"first_name": "",
|
||||||
|
"last_name": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"created": "2017-12-12T16:48:10.489550Z",
|
||||||
|
"modified": "2017-12-12T16:48:10.522189Z",
|
||||||
|
"user": 1,
|
||||||
|
"token": "kqHqxfpHGRRBXLNCOXxT5Zt3tpJogn",
|
||||||
|
"refresh_token": "miZq3hqSugvYxhzdQYJIBDgIHxJPnT",
|
||||||
|
"application": 4,
|
||||||
|
"expires": "2017-12-13T02:48:10.488180Z",
|
||||||
|
"scope": "read"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
For an OAuth token, the only fully mutable field is `scope`. The `application` field is *immutable
|
||||||
|
on update*, and all other fields are totally immutable, and will be auto-populated during creation:
|
||||||
|
`user` field will be the `user` field of related application; `expires` will be generated according
|
||||||
|
to Tower configuration setting `OAUTH2_PROVIDER`; `token` and `refresh_token` will be auto-generated
|
||||||
|
to be non-crashing random strings.
|
||||||
|
|
||||||
|
On RBAC side:
|
||||||
|
- A user will be able to create a token if they are able to see the related application;
|
||||||
|
- System admin is able to see and manipulate every token in the system;
|
||||||
|
- Organization admins will be able to see and manipulate all tokens belonging to Organization
|
||||||
|
members;
|
||||||
|
- Other normal users will only be able to see and manipulate their own tokens.
|
||||||
|
> Note: Users can only see the token or refresh-token _value_ at the time of creation ONLY.
|
||||||
|
|
||||||
|
#### Using OAuth 2 token system as a Personal Access Token (PAT)
|
||||||
|
The most common usage of OAuth 2 is authenticating users. The `token` field of a token is used
|
||||||
|
as part of the HTTP authentication header, in the format `Authorization: Bearer <token field value>`. This _Bearer_
|
||||||
|
token can be obtained by doing a curl to the `/api/o/token/` endpoint as shown in `api_o_auth_authorization_root_view.md`.
|
||||||
|
|
||||||
|
Here is an example of using that PAT to access an API endpoint using `curl`:
|
||||||
|
```
|
||||||
|
curl -H "Authorization: Bearer kqHqxfpHGRRBXLNCOXxT5Zt3tpJogn" http://localhost:8013/api/v2/credentials/
|
||||||
|
```
|
||||||
|
|
||||||
|
According to OAuth 2 specification, users should be able to acquire, revoke and refresh an access
|
||||||
|
token. In AWX the equivalent, and the easiest, way of doing that is creating a token, deleting
|
||||||
|
a token, and deleting a token quickly followed by creating a new one.
|
||||||
|
|
||||||
|
On the other hand, the specification also provides standard ways of doing those. RFC 6749 elaborates
|
||||||
|
on those topics, but in summary, an OAuth token is officially acquired via authorization using
|
||||||
|
authorization information provided by applications (special application fields mentioned above).
|
||||||
|
There are dedicated endpoints for authorization and acquiring tokens. The token acquire endpoint
|
||||||
|
is also responsible for token refresh, and token revoke is done by a dedicated token revoke endpoint.
|
||||||
|
|
||||||
|
In AWX, our OAuth system is built on top of
|
||||||
|
[Django Oauth Toolkit](https://django-oauth-toolkit.readthedocs.io/en/latest/), which provides full
|
||||||
|
support on standard authorization, token revoke and refresh. AWX implements them and puts related
|
||||||
|
endpoints under `/api/o/` endpoint. Detailed examples on the most typical usage of those endpoints
|
||||||
|
are available as description text of `/api/o/`.
|
||||||
|
|
||||||
|
#### Token scope mask over RBAC system
|
||||||
|
The scope of an OAuth token is a space-separated string composed of keywords like 'read' and 'write'.
|
||||||
|
These keywords are configurable and used to specify permission level of the authenticated API client.
|
||||||
|
For the initial OAuth implementation, we use the most simple scope configuration, where the only
|
||||||
|
valid scope keywords are 'read' and 'write'.
|
||||||
|
|
||||||
|
Read and write scopes provide a mask layer over the RBAC permission system of AWX. In specific, a
|
||||||
|
'write' scope gives the authenticated user full permissions the RBAC system provides, while 'read'
|
||||||
|
scope gives the authenticated user only read permissions the RBAC system provides.
|
||||||
|
|
||||||
|
For example, if a user has admin permission to a job template, she can both see and modify, launch
|
||||||
|
and delete the job template if authenticated via session or basic auth. On the other hand, if she
|
||||||
|
is authenticated using OAuth token, and the related token scope is 'read', she can only see but
|
||||||
|
not manipulate or launch the job template, despite she has admin role over it; if the token scope is
|
||||||
|
'write' or 'read write', she can take full advantage of the job template as its admin.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
* All CRUD operations for OAuth applications and tokens should function as described.
|
||||||
|
* RBAC rules applied to OAuth applications and tokens should behave as described.
|
||||||
|
* A default application should be auto-created for each new user.
|
||||||
|
* Incoming requests using unexpired OAuth token correctly in authentication header should be able
|
||||||
|
to successfully authenticate themselves.
|
||||||
|
* Token scope mask over RBAC should work as described.
|
||||||
|
* Tower configuration setting `OAUTH2_PROVIDER` should be configurable and function as described.
|
||||||
|
* `/api/o/` endpoint should work as expected. In specific, all examples given in the description
|
||||||
|
help text should be working (user following the steps should get expected result).
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
## Introduction
|
## Introduction
|
||||||
>> Updated to these docs coming soon.
|
|
||||||
|
|
||||||
Before Tower 3.3, auth token is used as the main authentication method. Starting from Tower 3.3,
|
Before Tower 3.3, auth token was used as the main authentication method. Starting from Tower 3.3,
|
||||||
session-based authentication will take the place as the main authentication, while auth token
|
session-based authentication will take the place as the main authentication method, and auth token
|
||||||
will be replaced by OAuth tokens also introduced in 3.3.
|
will be replaced by OAuth 2 tokens.
|
||||||
|
|
||||||
Session authentication is a safer way of utilizing HTTP(S) cookies:
|
Session authentication is a safer way of utilizing HTTP(S) cookies:
|
||||||
|
|
||||||
@@ -11,19 +10,19 @@ Theoretically, user can provide authentication information, like username and pa
|
|||||||
`Cookie` header, but this method is vulnerable to cookie hijacks, where crackers can see and steal user
|
`Cookie` header, but this method is vulnerable to cookie hijacks, where crackers can see and steal user
|
||||||
information from cookie payload.
|
information from cookie payload.
|
||||||
|
|
||||||
Session authentication, on the other hand, sets a single `sessionid` cookie, called 'session'. Session
|
Session authentication, on the other hand, sets a single `session_id` cookie. The session_id
|
||||||
is *a random string which will be mapped to user authentication informations by server*. Crackers who
|
is *a random string which will be mapped to user authentication informations by server*. Crackers who
|
||||||
hijacks cookie will only get session itself, which does not imply any critical user info, valid only for
|
hijacks cookie will only get the session_id itself, which does not imply any critical user info, is valid only for
|
||||||
a limited time, and can be revoked at any time.
|
a limited time, and can be revoked at any time.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
In session authentication, user log in using endpoint `/api/login/`. GET to `/api/login/` displays the
|
In session authentication, users log in using the `/api/login/` endpoint. A GET to `/api/login/` displays the
|
||||||
log in page of API browser:
|
log in page of API browser:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
User should enter correct username and password before clicking on 'LOG IN' button, which fires a POST
|
Users should enter correct username and password before clicking on 'LOG IN' button, which fires a POST
|
||||||
to `/api/login/` to actually log the user in. The return code of a successful login is 302, meaning upon
|
to `/api/login/` to actually log the user in. The return code of a successful login is 302, meaning upon
|
||||||
successful login, the browser will be redirected, the redirected destination is determined by `next` form
|
successful login, the browser will be redirected, the redirected destination is determined by `next` form
|
||||||
item described below.
|
item described below.
|
||||||
@@ -35,7 +34,7 @@ be provided in the form:
|
|||||||
* `next`: The path of the redirect destination, in API browser `"/api/"` is used.
|
* `next`: The path of the redirect destination, in API browser `"/api/"` is used.
|
||||||
* `csrfmiddlewaretoken`: The CSRF token, usually populated by using Django template `{% csrf_token %}`.
|
* `csrfmiddlewaretoken`: The CSRF token, usually populated by using Django template `{% csrf_token %}`.
|
||||||
|
|
||||||
Session is provided as a return `Set-Cookie` header. Here is a typical one:
|
The session_id is provided as a return `Set-Cookie` header. Here is a typical one:
|
||||||
```
|
```
|
||||||
Set-Cookie: sessionid=lwan8l5ynhrqvps280rg5upp7n3yp6ds; expires=Tue, 21-Nov-2017 16:33:13 GMT; httponly; Max-Age=1209600; Path=/
|
Set-Cookie: sessionid=lwan8l5ynhrqvps280rg5upp7n3yp6ds; expires=Tue, 21-Nov-2017 16:33:13 GMT; httponly; Max-Age=1209600; Path=/
|
||||||
```
|
```
|
||||||
@@ -45,10 +44,10 @@ session cookie value, expiration date, duration, etc.
|
|||||||
|
|
||||||
The duration of the cookie is configurable by Tower Configuration setting `SESSION_COOKIE_AGE` under
|
The duration of the cookie is configurable by Tower Configuration setting `SESSION_COOKIE_AGE` under
|
||||||
category `authentication`. It is an integer denoting the number of seconds the session cookie should
|
category `authentication`. It is an integer denoting the number of seconds the session cookie should
|
||||||
live.
|
live. The default session cookie age is 2 weeks.
|
||||||
|
|
||||||
After a valid session is acquired, a client should provide session as a cookie for subsequent requests
|
After a valid session is acquired, a client should provide the session_id as a cookie for subsequent requests
|
||||||
in order to be authenticated. like
|
in order to be authenticated. For example:
|
||||||
```
|
```
|
||||||
Cookie: sessionid=lwan8l5ynhrqvps280rg5upp7n3yp6ds; ...
|
Cookie: sessionid=lwan8l5ynhrqvps280rg5upp7n3yp6ds; ...
|
||||||
```
|
```
|
||||||
@@ -63,8 +62,7 @@ by performing session acquire with the session provided.
|
|||||||
|
|
||||||
A Tower configuration setting, `SESSIONS_PER_USER` under category `authentication`, is used to set the
|
A Tower configuration setting, `SESSIONS_PER_USER` under category `authentication`, is used to set the
|
||||||
maximum number of valid sessions a user can have at the same time. For example, if `SESSIONS_PER_USER`
|
maximum number of valid sessions a user can have at the same time. For example, if `SESSIONS_PER_USER`
|
||||||
is set to 3, while the same user is logged in via 5 different places, and thus have 5 valid sessions
|
is set to 3 and the same user is logged in from 5 different places, the earliest 2 sessions created will be invalidated. Tower will try
|
||||||
available at the same time, the earliest 2 (5 - 3) sessions created will be invalidated. Tower will try
|
|
||||||
broadcasting, via websocket, to all available clients. The websocket message body will contain a list of
|
broadcasting, via websocket, to all available clients. The websocket message body will contain a list of
|
||||||
invalidated sessions. If a client finds its session in that list, it should try logging out.
|
invalidated sessions. If a client finds its session in that list, it should try logging out.
|
||||||
|
|
||||||
@@ -79,7 +77,7 @@ is updated, all sessions she owned will be invalidated and deleted.
|
|||||||
* The maximum number of concurrent login for one user should be configurable by `SESSIONS_PER_USER`,
|
* The maximum number of concurrent login for one user should be configurable by `SESSIONS_PER_USER`,
|
||||||
and over-limit user sessions should be warned by websocket.
|
and over-limit user sessions should be warned by websocket.
|
||||||
* When a user's password is changed, all her sessions should be invalidated and deleted.
|
* When a user's password is changed, all her sessions should be invalidated and deleted.
|
||||||
* User should not be able to authenticate either HTTPS(S) request or websocket connect using invalid
|
* User should not be able to authenticate by HTTPS(S) request nor websocket connect using invalid
|
||||||
sessions.
|
sessions.
|
||||||
* No existing behavior, like job run, inventory update or callback receiver, should be affected
|
* No existing behavior, like job run, inventory update or callback receiver, should be affected
|
||||||
by session auth.
|
by session auth.
|
||||||
|
|||||||
Reference in New Issue
Block a user