mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-15 05:53:00 +00:00
Refactored scope names and updated docs to reflect this
This commit is contained in:
@@ -17,35 +17,37 @@ securityDefinitions:
|
|||||||
flow: accessCode
|
flow: accessCode
|
||||||
authorizationUrl: "/hub/api/oauth2/authorize" # what are the absolute URIs here? is oauth2 correct here or shall we use just authorizations?
|
authorizationUrl: "/hub/api/oauth2/authorize" # what are the absolute URIs here? is oauth2 correct here or shall we use just authorizations?
|
||||||
tokenUrl: "/hub/api/oauth2/token"
|
tokenUrl: "/hub/api/oauth2/token"
|
||||||
scopes:
|
scopes: # Todo: Generate based on scope table
|
||||||
|
(noscope): Allows only to identify the requesting entity
|
||||||
self: Metascope, grants access to user's own resources; resolves to (no scope) for services.
|
self: Metascope, grants access to user's own resources; resolves to (no scope) for services.
|
||||||
all: Metascope, valid for tokens only. Grants access to everything that the token's owning entity can do.
|
all: Metascope, valid for tokens only. Grants access to all resources of the token-owning entity.
|
||||||
admin:users: Grants read, write, create and delete access to users and their authentication state but not their servers or tokens.
|
admin:users: Grants read, write, create and delete access to users and their authentication state but not their servers or tokens.
|
||||||
admin:users:auth_state: Grants access to users' authentication state only.
|
admin:auth_state: Grants access to users' authentication state only.
|
||||||
users: Grants read and write permissions to users' models apart from servers, tokens and authentication state.
|
users: Grants read and write permissions to users' models apart from servers, tokens and authentication state.
|
||||||
users:activity: Grants access to read and post users' activity only.
|
users:activity: Grants access to read and post users' activity only.
|
||||||
users:activity!user=username: Update a single user's activity (example horizontal filter).
|
users:activity!user=username: Update a single user's activity (example horizontal filter).
|
||||||
read:users: Read-only access to users' models apart from servers, tokens and authentication state.
|
read:users: Read-only access to users' models apart from servers, tokens and authentication state.
|
||||||
read:users!user=username: As above limited to a specific user (example horizontal filter).
|
read:users!user=username: As above limited to a specific user (example horizontal filter).
|
||||||
read:users:name: Read-only access to users' names.
|
read:users:name: Read-only access to user names.
|
||||||
read:users:roles: Read-only access to a list of users' roles names.
|
read:roles:users: Read-only access to user role assignments.
|
||||||
read:users:groups: Read-only access to a list of users' group names.
|
read:users:groups: Read-only access to a list of users' group names.
|
||||||
read:users:activity: Read-only access to users' activity.
|
read:users:activity: Read-only access to users' activity.
|
||||||
read:users:activity!group=groupname: Read-only access to specific group's users' activity (example horizontal filter).
|
read:users:activity!group=groupname: Read-only access to specific group's users' activity (example horizontal filter).
|
||||||
admin:users:servers: Grants read, start/stop, create and delete permissions to users' servers and their state.
|
admin:servers: Grants read, start/stop, create and delete permissions to users' servers and their state.
|
||||||
admin:users:server_state: Grants access to servers' state only.
|
admin:server_state: Grants access to servers' state only.
|
||||||
users:servers: Allows for starting/stopping users' servers in addition to read access to their models. Does not include the server state.
|
servers: Allows for starting/stopping users' servers in addition to read access to their models. Does not include the server state.
|
||||||
users:servers!server=servername: Limits the above to a specific server (example horizontal filter).
|
servers!server=servername: Limits the above to a specific server (example horizontal filter).
|
||||||
read:users:servers: Read-only access to users' server models. Does not include the server state.
|
read:servers: Read-only access to users' server models. Does not include the server state.
|
||||||
users:tokens: Grants read, write, create and delete permissions to users' tokens.
|
tokens: Grants read, write, create and delete permissions to users' tokens.
|
||||||
read:users:tokens: Read-only access to users' tokens.
|
read:tokens: Read-only access to users' tokens.
|
||||||
admin:groups: Grants read, write, create and delete access to groups.
|
admin:groups: Grants read, write, create and delete access to groups.
|
||||||
groups: Grants read and write permissions to groups, including adding/removing users to/from groups.
|
groups: Grants read and write permissions to groups, including adding/removing users to/from groups.
|
||||||
|
read:roles:groups: Read-only access to group roles assignments
|
||||||
groups!group=groupname: As above limited to a specific group only (example horizontal filter)
|
groups!group=groupname: As above limited to a specific group only (example horizontal filter)
|
||||||
read:groups: Read-only access to groups.
|
read:groups: Read-only access to groups.
|
||||||
read:services: Read-only access to service models.
|
read:services: Read-only access to service models.
|
||||||
read:services:name: Read-only access to service names.
|
read:services:name: Read-only access to service names.
|
||||||
read:services:roles: Read-only access to a list of service roles names.
|
read:roles:services: Read-only access to a list of service roles names.
|
||||||
read:hub: Read-only access to detailed information about JupyterHub.
|
read:hub: Read-only access to detailed information about JupyterHub.
|
||||||
proxy: Allows for obtaining information about the proxy's routing table, for syncing the Hub with proxy and notifying the Hub about a new proxy.
|
proxy: Allows for obtaining information about the proxy's routing table, for syncing the Hub with proxy and notifying the Hub about a new proxy.
|
||||||
shutdown: Grants access to shutdown the Hub.
|
shutdown: Grants access to shutdown the Hub.
|
||||||
@@ -126,8 +128,10 @@ paths:
|
|||||||
- read:users:name
|
- read:users:name
|
||||||
- read:users:groups
|
- read:users:groups
|
||||||
- read:users:activity
|
- read:users:activity
|
||||||
- read:users:servers
|
- read:servers
|
||||||
#TODO: add admin:users:auth_state/server_state?
|
- read:roles:users
|
||||||
|
- admin:auth_state
|
||||||
|
- admin:server_state
|
||||||
parameters:
|
parameters:
|
||||||
- name: state
|
- name: state
|
||||||
in: query
|
in: query
|
||||||
@@ -203,9 +207,10 @@ paths:
|
|||||||
- read:users:name
|
- read:users:name
|
||||||
- read:users:groups
|
- read:users:groups
|
||||||
- read:users:activity
|
- read:users:activity
|
||||||
- read:users:servers
|
- read:servers
|
||||||
- admin:users:auth_state
|
- read:roles:users
|
||||||
- admin:users:server_state
|
- admin:auth_state
|
||||||
|
- admin:server_state
|
||||||
parameters:
|
parameters:
|
||||||
- name: name
|
- name: name
|
||||||
description: username
|
description: username
|
||||||
@@ -340,7 +345,7 @@ paths:
|
|||||||
summary: Start a user's single-user notebook server
|
summary: Start a user's single-user notebook server
|
||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- users:servers
|
- servers
|
||||||
parameters:
|
parameters:
|
||||||
- name: name
|
- name: name
|
||||||
description: username
|
description: username
|
||||||
@@ -369,7 +374,7 @@ paths:
|
|||||||
summary: Stop a user's server
|
summary: Stop a user's server
|
||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- users:servers
|
- servers
|
||||||
parameters:
|
parameters:
|
||||||
- name: name
|
- name: name
|
||||||
description: username
|
description: username
|
||||||
@@ -386,7 +391,7 @@ paths:
|
|||||||
summary: Start a user's single-user named-server notebook server
|
summary: Start a user's single-user named-server notebook server
|
||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- users:servers
|
- servers
|
||||||
parameters:
|
parameters:
|
||||||
- name: name
|
- name: name
|
||||||
description: username
|
description: username
|
||||||
@@ -420,7 +425,7 @@ paths:
|
|||||||
summary: Stop a user's named-server
|
summary: Stop a user's named-server
|
||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- users:servers
|
- servers
|
||||||
parameters:
|
parameters:
|
||||||
- name: name
|
- name: name
|
||||||
description: username
|
description: username
|
||||||
@@ -460,7 +465,7 @@ paths:
|
|||||||
summary: List tokens for the user
|
summary: List tokens for the user
|
||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- read:users:tokens
|
- read:tokens
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: The list of tokens
|
description: The list of tokens
|
||||||
@@ -476,7 +481,7 @@ paths:
|
|||||||
summary: Create a new token for the user
|
summary: Create a new token for the user
|
||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- users:tokens
|
- tokens
|
||||||
parameters:
|
parameters:
|
||||||
- name: token_params
|
- name: token_params
|
||||||
in: body
|
in: body
|
||||||
@@ -519,7 +524,7 @@ paths:
|
|||||||
summary: Get the model for a token by id
|
summary: Get the model for a token by id
|
||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- read:users:tokens
|
- read:tokens
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: The info for the new token
|
description: The info for the new token
|
||||||
@@ -529,7 +534,7 @@ paths:
|
|||||||
summary: Delete (revoke) a token by id
|
summary: Delete (revoke) a token by id
|
||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- users:tokens
|
- tokens
|
||||||
responses:
|
responses:
|
||||||
"204":
|
"204":
|
||||||
description: The token has been deleted
|
description: The token has been deleted
|
||||||
@@ -542,9 +547,10 @@ paths:
|
|||||||
- read:users:name
|
- read:users:name
|
||||||
- read:users:groups
|
- read:users:groups
|
||||||
- read:users:activity
|
- read:users:activity
|
||||||
- read:users:servers
|
- read:servers
|
||||||
- admin:users:auth_state
|
- read:roles:users
|
||||||
- admin:users:server_state
|
- admin:auth_state
|
||||||
|
- admin:server_state
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: The authenticated user's model is returned.
|
description: The authenticated user's model is returned.
|
||||||
@@ -556,6 +562,8 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- read:groups
|
- read:groups
|
||||||
|
- read:groups:name
|
||||||
|
- read:roles:groups
|
||||||
parameters:
|
parameters:
|
||||||
- name: offset
|
- name: offset
|
||||||
in: query
|
in: query
|
||||||
@@ -586,6 +594,8 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- read:groups
|
- read:groups
|
||||||
|
- read:groups:name
|
||||||
|
- read:roles:groups
|
||||||
parameters:
|
parameters:
|
||||||
- name: name
|
- name: name
|
||||||
description: group name
|
description: group name
|
||||||
@@ -688,6 +698,8 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- read:services
|
- read:services
|
||||||
|
- read:services:name
|
||||||
|
- read:roles:services
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: The service list
|
description: The service list
|
||||||
@@ -701,6 +713,8 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- read:services
|
- read:services
|
||||||
|
- read:services:name
|
||||||
|
- read:roles:services
|
||||||
parameters:
|
parameters:
|
||||||
- name: name
|
- name: name
|
||||||
description: service name
|
description: service name
|
||||||
@@ -790,7 +804,7 @@ paths:
|
|||||||
accepts passwords (e.g. not OAuth).
|
accepts passwords (e.g. not OAuth).
|
||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- users:tokens # minrk: this is a deprecated alias to POST /users/{name}/tokens, either remove it or use the same scope
|
- tokens
|
||||||
parameters:
|
parameters:
|
||||||
- name: credentials
|
- name: credentials
|
||||||
in: body
|
in: body
|
||||||
@@ -817,7 +831,7 @@ paths:
|
|||||||
summary: Identify a user or service from an API token
|
summary: Identify a user or service from an API token
|
||||||
security:
|
security:
|
||||||
- oauth2:
|
- oauth2:
|
||||||
- read:users:tokens # minrk: is it really necessary to have a scope for this, or use self handler for token whoami?
|
- (noscope)
|
||||||
parameters:
|
parameters:
|
||||||
- name: token
|
- name: token
|
||||||
in: path
|
in: path
|
||||||
|
@@ -17,7 +17,7 @@ To remedy situations like this, JupyterHub is transitioning to an RBAC system. B
|
|||||||
|
|
||||||
## Definitions
|
## Definitions
|
||||||
|
|
||||||
**Scopes** are specific permissions used to evaluate API requests. For example: the API endpoint `users/servers`, which enables starting or stopping user servers, is guarded by the scope `users:servers`.
|
**Scopes** are specific permissions used to evaluate API requests. For example: the API endpoint `users/servers`, which enables starting or stopping user servers, is guarded by the scope `servers`.
|
||||||
|
|
||||||
Scopes are not directly assigned to requesters. Rather, when a client performs an API call, their access will be evaluated based on their assigned roles.
|
Scopes are not directly assigned to requesters. Rather, when a client performs an API call, their access will be evaluated based on their assigned roles.
|
||||||
|
|
||||||
|
@@ -55,7 +55,7 @@ c.JupyterHub.load_roles = [
|
|||||||
{
|
{
|
||||||
'name': 'server-rights',
|
'name': 'server-rights',
|
||||||
'description': 'Allows parties to start and stop user servers',
|
'description': 'Allows parties to start and stop user servers',
|
||||||
'scopes': ['users:servers'],
|
'scopes': ['servers'],
|
||||||
'users': ['alice', 'bob'],
|
'users': ['alice', 'bob'],
|
||||||
'services': ['idle-culler'],
|
'services': ['idle-culler'],
|
||||||
'groups': ['admin-group'],
|
'groups': ['admin-group'],
|
||||||
|
@@ -7,7 +7,7 @@ A scope has a syntax-based design that reveals which resources it provides acces
|
|||||||
## Scope conventions
|
## Scope conventions
|
||||||
|
|
||||||
- `<resource>` \
|
- `<resource>` \
|
||||||
The `<resource>` scopes, such as `users` or `groups`, grant read and write permissions to the resource itself and all its sub-resources. E.g., the scope `users:servers` is included within the scope `users`.
|
The top-level `<resource>` scopes, such as `users` or `groups`, grant read and write permissions to the resource itself as well as its sub-resources. For example, the scope `users:activity` is included in the scope `users`.
|
||||||
+++
|
+++
|
||||||
|
|
||||||
- `read:<resource>` \
|
- `read:<resource>` \
|
||||||
@@ -19,7 +19,7 @@ A scope has a syntax-based design that reveals which resources it provides acces
|
|||||||
+++
|
+++
|
||||||
|
|
||||||
- `<resource>:<subresource>` \
|
- `<resource>:<subresource>` \
|
||||||
The {ref}`vertically filtered <vertical-filtering-target>` scopes provide access to a subset of the information granted by the `<resource>` scope. E.g., the scope `users:servers` allows for accessing user servers only.
|
The {ref}`vertically filtered <vertical-filtering-target>` scopes provide access to a subset of the information granted by the `<resource>` scope. E.g., the scope `users:activity` only provides permission to post user activity.
|
||||||
+++
|
+++
|
||||||
|
|
||||||
- `<resource>!<object>=<objectname>` \
|
- `<resource>!<object>=<objectname>` \
|
||||||
@@ -42,8 +42,8 @@ Metascopes do not follow the general scope syntax. Instead, a metascope resolves
|
|||||||
Access to the user's own resources and subresources is covered by metascope `self`. This metascope includes the user's model, activity, servers and tokens. For example, `self` for a user named "gerard" includes:
|
Access to the user's own resources and subresources is covered by metascope `self`. This metascope includes the user's model, activity, servers and tokens. For example, `self` for a user named "gerard" includes:
|
||||||
|
|
||||||
- `users!user=gerard` where the `users` scope provides access to the full user model and activity. The filter restricts this access to the user's own resources.
|
- `users!user=gerard` where the `users` scope provides access to the full user model and activity. The filter restricts this access to the user's own resources.
|
||||||
- `users:servers!user=gerard` which grants the user access to their own servers without being able to create/delete any.
|
- `servers!user=gerard` which grants the user access to their own servers without being able to create/delete any.
|
||||||
- `users:tokens!user=gerard` which allows the user to access, request and delete their own tokens.
|
- `tokens!user=gerard` which allows the user to access, request and delete their own tokens.
|
||||||
|
|
||||||
The `self` scope is only valid for user entities. In other cases (e.g., for services) it resolves to an empty set of scopes.
|
The `self` scope is only valid for user entities. In other cases (e.g., for services) it resolves to an empty set of scopes.
|
||||||
|
|
||||||
|
@@ -38,7 +38,7 @@ Below follows a short tutorial on how to add a cull-idle service in the RBAC sys
|
|||||||
{
|
{
|
||||||
"name": "idle-culler",
|
"name": "idle-culler",
|
||||||
"description": "Culls idle servers",
|
"description": "Culls idle servers",
|
||||||
"scopes": ["read:users:name", "read:users:activity", "users:servers"],
|
"scopes": ["read:users:name", "read:users:activity", "servers"],
|
||||||
"services": ["idle-culler"],
|
"services": ["idle-culler"],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -48,7 +48,7 @@ Below follows a short tutorial on how to add a cull-idle service in the RBAC sys
|
|||||||
Note that in the RBAC system the `admin` field in the `idle-culler` service definition is omitted. Instead, the `idle-culler` role provides the service with only the permissions it needs.
|
Note that in the RBAC system the `admin` field in the `idle-culler` service definition is omitted. Instead, the `idle-culler` role provides the service with only the permissions it needs.
|
||||||
|
|
||||||
If the optional actions of deleting the idle servers and/or removing inactive users are desired, **change the following scopes** in the `idle-culler` role definition:
|
If the optional actions of deleting the idle servers and/or removing inactive users are desired, **change the following scopes** in the `idle-culler` role definition:
|
||||||
- `users:servers` to `admin:users:servers` for deleting servers
|
- `servers` to `admin:servers` for deleting servers
|
||||||
- `read:users:name`, `read:users:activity` to `admin:users` for deleting users.
|
- `read:users:name`, `read:users:activity` to `admin:users` for deleting users.
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -65,8 +65,8 @@ A service capable of creating/removing users and launching multiple servers shou
|
|||||||
The scopes required to access the API enpoints:
|
The scopes required to access the API enpoints:
|
||||||
|
|
||||||
1. `admin:users`
|
1. `admin:users`
|
||||||
2. `users:servers`
|
2. `servers`
|
||||||
3. `admin:users:servers`
|
3. `admin:servers`
|
||||||
|
|
||||||
From the above, the role definition is:
|
From the above, the role definition is:
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ c.JupyterHub.load_roles = [
|
|||||||
{
|
{
|
||||||
"name": "api-launcher",
|
"name": "api-launcher",
|
||||||
"description": "Manages servers",
|
"description": "Manages servers",
|
||||||
"scopes": ["admin:users", "admin:users:servers"],
|
"scopes": ["admin:users", "admin:servers"],
|
||||||
"services": [<service_name>]
|
"services": [<service_name>]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -117,7 +117,7 @@ c.JupyterHub.load_roles = [
|
|||||||
{
|
{
|
||||||
'name': 'teacher',
|
'name': 'teacher',
|
||||||
'description': 'Allows for accessing information about teacher group members and starting/stopping their servers',
|
'description': 'Allows for accessing information about teacher group members and starting/stopping their servers',
|
||||||
'scopes': [ 'read:users!group=class-B', 'users:servers!group=class-B'],
|
'scopes': [ 'read:users!group=class-B', 'servers!group=class-B'],
|
||||||
'users': ['johan']
|
'users': ['johan']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@@ -91,7 +91,7 @@ c.JupyterHub.load_roles = [
|
|||||||
"name": "idle-culler",
|
"name": "idle-culler",
|
||||||
"scopes": [
|
"scopes": [
|
||||||
"read:users:activity", # read user last_activity
|
"read:users:activity", # read user last_activity
|
||||||
"users:servers", # start and stop servers
|
"servers", # start and stop servers
|
||||||
# 'admin:users' # needed if culling idle users as well
|
# 'admin:users' # needed if culling idle users as well
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -45,7 +45,7 @@ $ curl -X GET http://127.0.0.1:8000/services/fastapi/me \
|
|||||||
"servers": null,
|
"servers": null,
|
||||||
"scopes": [
|
"scopes": [
|
||||||
"access:services",
|
"access:services",
|
||||||
"access:users:servers!user=test-user",
|
"access:servers!user=test-user",
|
||||||
"...",
|
"...",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -43,19 +43,19 @@ $ curl -H "Authorization: token 8630bbd8ef064c48b22c7f122f0cd8ad" http://127.0.0
|
|||||||
],
|
],
|
||||||
"scopes": [
|
"scopes": [
|
||||||
"access:services",
|
"access:services",
|
||||||
"access:users:servers!user=test",
|
"access:servers!user=test",
|
||||||
"read:users!user=test",
|
"read:users!user=test",
|
||||||
"read:users:activity!user=test",
|
"read:users:activity!user=test",
|
||||||
"read:users:groups!user=test",
|
"read:users:groups!user=test",
|
||||||
"read:users:name!user=test",
|
"read:users:name!user=test",
|
||||||
"read:users:servers!user=test",
|
"read:servers!user=test",
|
||||||
"read:users:tokens!user=test",
|
"read:tokens!user=test",
|
||||||
"users!user=test",
|
"users!user=test",
|
||||||
"users:activity!user=test",
|
"users:activity!user=test",
|
||||||
"users:groups!user=test",
|
"users:groups!user=test",
|
||||||
"users:name!user=test",
|
"users:name!user=test",
|
||||||
"users:servers!user=test",
|
"servers!user=test",
|
||||||
"users:tokens!user=test"
|
"tokens!user=test"
|
||||||
],
|
],
|
||||||
"server": null
|
"server": null
|
||||||
}
|
}
|
||||||
|
@@ -258,7 +258,7 @@ class OAuthAuthorizeHandler(OAuthHandler, BaseHandler):
|
|||||||
|
|
||||||
# check for access to target resource
|
# check for access to target resource
|
||||||
if client.spawner:
|
if client.spawner:
|
||||||
scope_filter = self.get_scope_filter("access:users:servers")
|
scope_filter = self.get_scope_filter("access:servers")
|
||||||
allowed = scope_filter(client.spawner, kind='server')
|
allowed = scope_filter(client.spawner, kind='server')
|
||||||
elif client.service:
|
elif client.service:
|
||||||
scope_filter = self.get_scope_filter("access:services")
|
scope_filter = self.get_scope_filter("access:services")
|
||||||
|
@@ -143,7 +143,7 @@ class APIHandler(BaseHandler):
|
|||||||
'user_options': spawner.user_options,
|
'user_options': spawner.user_options,
|
||||||
'progress_url': spawner._progress_url,
|
'progress_url': spawner._progress_url,
|
||||||
}
|
}
|
||||||
scope_filter = self.get_scope_filter('admin:users:server_state')
|
scope_filter = self.get_scope_filter('admin:server_state')
|
||||||
if scope_filter(spawner, kind='server'):
|
if scope_filter(spawner, kind='server'):
|
||||||
model['state'] = spawner.get_state()
|
model['state'] = spawner.get_state()
|
||||||
return model
|
return model
|
||||||
@@ -219,9 +219,9 @@ class APIHandler(BaseHandler):
|
|||||||
'read:users:name': {'kind', 'name', 'admin'},
|
'read:users:name': {'kind', 'name', 'admin'},
|
||||||
'read:users:groups': {'kind', 'name', 'groups'},
|
'read:users:groups': {'kind', 'name', 'groups'},
|
||||||
'read:users:activity': {'kind', 'name', 'last_activity'},
|
'read:users:activity': {'kind', 'name', 'last_activity'},
|
||||||
'read:users:servers': {'kind', 'name', 'servers'},
|
'read:servers': {'kind', 'name', 'servers'},
|
||||||
'read:users:roles': {'kind', 'name', 'roles', 'admin'},
|
'read:roles:users': {'kind', 'name', 'roles', 'admin'},
|
||||||
'admin:users:auth_state': {'kind', 'name', 'auth_state'},
|
'admin:auth_state': {'kind', 'name', 'auth_state'},
|
||||||
}
|
}
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"Asking for user model of %s with scopes [%s]",
|
"Asking for user model of %s with scopes [%s]",
|
||||||
@@ -237,7 +237,7 @@ class APIHandler(BaseHandler):
|
|||||||
model['pending'] = user.spawners[''].pending
|
model['pending'] = user.spawners[''].pending
|
||||||
|
|
||||||
servers = model['servers'] = {}
|
servers = model['servers'] = {}
|
||||||
scope_filter = self.get_scope_filter('read:users:servers')
|
scope_filter = self.get_scope_filter('read:servers')
|
||||||
for name, spawner in user.spawners.items():
|
for name, spawner in user.spawners.items():
|
||||||
# include 'active' servers, not just ready
|
# include 'active' servers, not just ready
|
||||||
# (this includes pending events)
|
# (this includes pending events)
|
||||||
@@ -258,7 +258,7 @@ class APIHandler(BaseHandler):
|
|||||||
access_map = {
|
access_map = {
|
||||||
'read:groups': {'kind', 'name', 'users'},
|
'read:groups': {'kind', 'name', 'users'},
|
||||||
'read:groups:name': {'kind', 'name'},
|
'read:groups:name': {'kind', 'name'},
|
||||||
'read:groups:roles': {'kind', 'name', 'roles'},
|
'read:roles:groups': {'kind', 'name', 'roles'},
|
||||||
}
|
}
|
||||||
model = self._filter_model(model, access_map, group, 'group')
|
model = self._filter_model(model, access_map, group, 'group')
|
||||||
return model
|
return model
|
||||||
@@ -290,7 +290,7 @@ class APIHandler(BaseHandler):
|
|||||||
'display',
|
'display',
|
||||||
},
|
},
|
||||||
'read:services:name': {'kind', 'name', 'admin'},
|
'read:services:name': {'kind', 'name', 'admin'},
|
||||||
'read:services:roles': {'kind', 'name', 'roles', 'admin'},
|
'read:roles:services': {'kind', 'name', 'roles', 'admin'},
|
||||||
}
|
}
|
||||||
model = self._filter_model(model, access_map, service, 'service')
|
model = self._filter_model(model, access_map, service, 'service')
|
||||||
return model
|
return model
|
||||||
|
@@ -34,7 +34,7 @@ class _GroupAPIHandler(APIHandler):
|
|||||||
|
|
||||||
|
|
||||||
class GroupListAPIHandler(_GroupAPIHandler):
|
class GroupListAPIHandler(_GroupAPIHandler):
|
||||||
@needs_scope('read:groups', 'read:groups:name', 'read:groups:roles')
|
@needs_scope('read:groups', 'read:groups:name', 'read:roles:groups')
|
||||||
def get(self):
|
def get(self):
|
||||||
"""List groups"""
|
"""List groups"""
|
||||||
query = self.db.query(orm.Group)
|
query = self.db.query(orm.Group)
|
||||||
@@ -77,7 +77,7 @@ class GroupListAPIHandler(_GroupAPIHandler):
|
|||||||
class GroupAPIHandler(_GroupAPIHandler):
|
class GroupAPIHandler(_GroupAPIHandler):
|
||||||
"""View and modify groups by name"""
|
"""View and modify groups by name"""
|
||||||
|
|
||||||
@needs_scope('read:groups', 'read:groups:name', 'read:groups:roles')
|
@needs_scope('read:groups', 'read:groups:name', 'read:roles:groups')
|
||||||
def get(self, group_name):
|
def get(self, group_name):
|
||||||
group = self.find_group(group_name)
|
group = self.find_group(group_name)
|
||||||
self.write(json.dumps(self.group_model(group)))
|
self.write(json.dumps(self.group_model(group)))
|
||||||
|
@@ -11,7 +11,7 @@ from .base import APIHandler
|
|||||||
|
|
||||||
|
|
||||||
class ServiceListAPIHandler(APIHandler):
|
class ServiceListAPIHandler(APIHandler):
|
||||||
@needs_scope('read:services', 'read:services:name', 'read:services:roles')
|
@needs_scope('read:services', 'read:services:name', 'read:roles:services')
|
||||||
def get(self):
|
def get(self):
|
||||||
data = {}
|
data = {}
|
||||||
for name, service in self.services.items():
|
for name, service in self.services.items():
|
||||||
@@ -22,7 +22,7 @@ class ServiceListAPIHandler(APIHandler):
|
|||||||
|
|
||||||
|
|
||||||
class ServiceAPIHandler(APIHandler):
|
class ServiceAPIHandler(APIHandler):
|
||||||
@needs_scope('read:services', 'read:services:name', 'read:services:roles')
|
@needs_scope('read:services', 'read:services:name', 'read:roles:services')
|
||||||
def get(self, service_name):
|
def get(self, service_name):
|
||||||
service = self.services[service_name]
|
service = self.services[service_name]
|
||||||
self.write(json.dumps(self.service_model(service)))
|
self.write(json.dumps(self.service_model(service)))
|
||||||
|
@@ -75,10 +75,10 @@ class UserListAPIHandler(APIHandler):
|
|||||||
@needs_scope(
|
@needs_scope(
|
||||||
'read:users',
|
'read:users',
|
||||||
'read:users:name',
|
'read:users:name',
|
||||||
'read:users:servers',
|
'read:servers',
|
||||||
'read:users:groups',
|
'read:users:groups',
|
||||||
'read:users:activity',
|
'read:users:activity',
|
||||||
'read:users:roles',
|
'read:roles:users',
|
||||||
)
|
)
|
||||||
def get(self):
|
def get(self):
|
||||||
state_filter = self.get_argument("state", None)
|
state_filter = self.get_argument("state", None)
|
||||||
@@ -195,10 +195,10 @@ class UserAPIHandler(APIHandler):
|
|||||||
@needs_scope(
|
@needs_scope(
|
||||||
'read:users',
|
'read:users',
|
||||||
'read:users:name',
|
'read:users:name',
|
||||||
'read:users:servers',
|
'read:servers',
|
||||||
'read:users:groups',
|
'read:users:groups',
|
||||||
'read:users:activity',
|
'read:users:activity',
|
||||||
'read:users:roles',
|
'read:roles:users',
|
||||||
)
|
)
|
||||||
async def get(self, user_name):
|
async def get(self, user_name):
|
||||||
user = self.find_user(user_name)
|
user = self.find_user(user_name)
|
||||||
@@ -297,7 +297,7 @@ class UserAPIHandler(APIHandler):
|
|||||||
class UserTokenListAPIHandler(APIHandler):
|
class UserTokenListAPIHandler(APIHandler):
|
||||||
"""API endpoint for listing/creating tokens"""
|
"""API endpoint for listing/creating tokens"""
|
||||||
|
|
||||||
@needs_scope('read:users:tokens')
|
@needs_scope('read:tokens')
|
||||||
def get(self, user_name):
|
def get(self, user_name):
|
||||||
"""Get tokens for a given user"""
|
"""Get tokens for a given user"""
|
||||||
user = self.find_user(user_name)
|
user = self.find_user(user_name)
|
||||||
@@ -352,7 +352,7 @@ class UserTokenListAPIHandler(APIHandler):
|
|||||||
self._resolve_roles_and_scopes()
|
self._resolve_roles_and_scopes()
|
||||||
user = self.find_user(user_name)
|
user = self.find_user(user_name)
|
||||||
kind = 'user' if isinstance(requester, User) else 'service'
|
kind = 'user' if isinstance(requester, User) else 'service'
|
||||||
scope_filter = self.get_scope_filter('users:tokens')
|
scope_filter = self.get_scope_filter('tokens')
|
||||||
if user is None or not scope_filter(user, kind):
|
if user is None or not scope_filter(user, kind):
|
||||||
raise web.HTTPError(
|
raise web.HTTPError(
|
||||||
403,
|
403,
|
||||||
@@ -417,7 +417,7 @@ class UserTokenAPIHandler(APIHandler):
|
|||||||
raise web.HTTPError(404, "Token not found %s", orm_token)
|
raise web.HTTPError(404, "Token not found %s", orm_token)
|
||||||
return orm_token
|
return orm_token
|
||||||
|
|
||||||
@needs_scope('read:users:tokens')
|
@needs_scope('read:tokens')
|
||||||
def get(self, user_name, token_id):
|
def get(self, user_name, token_id):
|
||||||
""""""
|
""""""
|
||||||
user = self.find_user(user_name)
|
user = self.find_user(user_name)
|
||||||
@@ -426,7 +426,7 @@ class UserTokenAPIHandler(APIHandler):
|
|||||||
token = self.find_token_by_id(user, token_id)
|
token = self.find_token_by_id(user, token_id)
|
||||||
self.write(json.dumps(self.token_model(token)))
|
self.write(json.dumps(self.token_model(token)))
|
||||||
|
|
||||||
@needs_scope('users:tokens')
|
@needs_scope('tokens')
|
||||||
def delete(self, user_name, token_id):
|
def delete(self, user_name, token_id):
|
||||||
"""Delete a token"""
|
"""Delete a token"""
|
||||||
user = self.find_user(user_name)
|
user = self.find_user(user_name)
|
||||||
@@ -451,7 +451,7 @@ class UserTokenAPIHandler(APIHandler):
|
|||||||
class UserServerAPIHandler(APIHandler):
|
class UserServerAPIHandler(APIHandler):
|
||||||
"""Start and stop single-user servers"""
|
"""Start and stop single-user servers"""
|
||||||
|
|
||||||
@needs_scope('users:servers')
|
@needs_scope('servers')
|
||||||
async def post(self, user_name, server_name=''):
|
async def post(self, user_name, server_name=''):
|
||||||
user = self.find_user(user_name)
|
user = self.find_user(user_name)
|
||||||
if server_name:
|
if server_name:
|
||||||
@@ -496,7 +496,7 @@ class UserServerAPIHandler(APIHandler):
|
|||||||
self.set_header('Content-Type', 'text/plain')
|
self.set_header('Content-Type', 'text/plain')
|
||||||
self.set_status(status)
|
self.set_status(status)
|
||||||
|
|
||||||
@needs_scope('users:servers')
|
@needs_scope('servers')
|
||||||
async def delete(self, user_name, server_name=''):
|
async def delete(self, user_name, server_name=''):
|
||||||
user = self.find_user(user_name)
|
user = self.find_user(user_name)
|
||||||
options = self.get_json_body()
|
options = self.get_json_body()
|
||||||
@@ -569,7 +569,7 @@ class UserAdminAccessAPIHandler(APIHandler):
|
|||||||
This handler sets the necessary cookie for an admin to login to a single-user server.
|
This handler sets the necessary cookie for an admin to login to a single-user server.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@needs_scope('users:servers')
|
@needs_scope('servers')
|
||||||
def post(self, user_name):
|
def post(self, user_name):
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"Deprecated in JupyterHub 0.8."
|
"Deprecated in JupyterHub 0.8."
|
||||||
@@ -625,7 +625,7 @@ class SpawnProgressAPIHandler(APIHandler):
|
|||||||
|
|
||||||
await asyncio.wait([self._finish_future], timeout=self.keepalive_interval)
|
await asyncio.wait([self._finish_future], timeout=self.keepalive_interval)
|
||||||
|
|
||||||
@needs_scope('read:users:servers')
|
@needs_scope('read:servers')
|
||||||
async def get(self, user_name, server_name=''):
|
async def get(self, user_name, server_name=''):
|
||||||
self.set_header('Cache-Control', 'no-cache')
|
self.set_header('Cache-Control', 'no-cache')
|
||||||
if server_name is None:
|
if server_name is None:
|
||||||
|
@@ -456,7 +456,7 @@ class AdminHandler(BaseHandler):
|
|||||||
@web.authenticated
|
@web.authenticated
|
||||||
@needs_scope('users')
|
@needs_scope('users')
|
||||||
@needs_scope('admin:users')
|
@needs_scope('admin:users')
|
||||||
@needs_scope('admin:users:servers')
|
@needs_scope('admin:servers')
|
||||||
async def get(self):
|
async def get(self):
|
||||||
auth_state = await self.current_user.get_auth_state()
|
auth_state = await self.current_user.get_auth_state()
|
||||||
html = await self.render_template(
|
html = await self.render_template(
|
||||||
|
@@ -33,16 +33,16 @@ def get_default_roles():
|
|||||||
'description': 'Elevated privileges (can do anything)',
|
'description': 'Elevated privileges (can do anything)',
|
||||||
'scopes': [
|
'scopes': [
|
||||||
'admin:users',
|
'admin:users',
|
||||||
'admin:users:servers',
|
'admin:servers',
|
||||||
'users:tokens',
|
'tokens',
|
||||||
'admin:groups',
|
'admin:groups',
|
||||||
'read:services',
|
'read:services',
|
||||||
'read:hub',
|
'read:hub',
|
||||||
'proxy',
|
'proxy',
|
||||||
'shutdown',
|
'shutdown',
|
||||||
'access:services',
|
'access:services',
|
||||||
'access:users:servers',
|
'access:servers',
|
||||||
'read:services:roles',
|
'read:roles',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -50,7 +50,7 @@ def get_default_roles():
|
|||||||
'description': 'Post activity only',
|
'description': 'Post activity only',
|
||||||
'scopes': [
|
'scopes': [
|
||||||
'users:activity!user',
|
'users:activity!user',
|
||||||
'access:users:servers!user',
|
'access:servers!user',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -70,9 +70,10 @@ def expand_self_scope(name):
|
|||||||
users:name
|
users:name
|
||||||
users:groups
|
users:groups
|
||||||
users:activity
|
users:activity
|
||||||
users:servers
|
tokens
|
||||||
users:tokens
|
servers
|
||||||
access:users:servers
|
access:servers
|
||||||
|
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
name (str): user name
|
name (str): user name
|
||||||
@@ -87,11 +88,11 @@ def expand_self_scope(name):
|
|||||||
'read:users:groups',
|
'read:users:groups',
|
||||||
'users:activity',
|
'users:activity',
|
||||||
'read:users:activity',
|
'read:users:activity',
|
||||||
'users:servers',
|
'servers',
|
||||||
'read:users:servers',
|
'read:servers',
|
||||||
'users:tokens',
|
'tokens',
|
||||||
'read:users:tokens',
|
'read:tokens',
|
||||||
'access:users:servers',
|
'access:servers',
|
||||||
]
|
]
|
||||||
return {"{}!user={}".format(scope, name) for scope in scope_list}
|
return {"{}!user={}".format(scope, name) for scope in scope_list}
|
||||||
|
|
||||||
|
@@ -34,9 +34,9 @@ scope_definitions = {
|
|||||||
},
|
},
|
||||||
'admin:users': {
|
'admin:users': {
|
||||||
'description': 'Read, write, create and delete users and their authentication state, not including their servers or tokens.',
|
'description': 'Read, write, create and delete users and their authentication state, not including their servers or tokens.',
|
||||||
'subscopes': ['admin:users:auth_state', 'users', 'read:users:roles'],
|
'subscopes': ['admin:auth_state', 'users', 'read:roles:users'],
|
||||||
},
|
},
|
||||||
'admin:users:auth_state': {'description': 'Read a user’s authentication state.'},
|
'admin:auth_state': {'description': 'Read a user’s authentication state.'},
|
||||||
'users': {
|
'users': {
|
||||||
'description': 'Read and write permissions to user models (excluding servers, tokens and authentication state).',
|
'description': 'Read and write permissions to user models (excluding servers, tokens and authentication state).',
|
||||||
'subscopes': ['read:users', 'users:activity'],
|
'subscopes': ['read:users', 'users:activity'],
|
||||||
@@ -52,32 +52,38 @@ scope_definitions = {
|
|||||||
'read:users:name': {'description': 'Read names of users.'},
|
'read:users:name': {'description': 'Read names of users.'},
|
||||||
'read:users:groups': {'description': 'Read users’ group membership.'},
|
'read:users:groups': {'description': 'Read users’ group membership.'},
|
||||||
'read:users:activity': {'description': 'Read time of last user activity.'},
|
'read:users:activity': {'description': 'Read time of last user activity.'},
|
||||||
'read:users:roles': {'description': 'Read users’ role assignments.'},
|
'read:roles': {
|
||||||
|
'description': 'Read role assignments.',
|
||||||
|
'subscopes': ['read:roles:users', 'read:roles:services', 'read:roles:groups'],
|
||||||
|
},
|
||||||
|
'read:roles:users': {'description': 'Read user role assignments.'},
|
||||||
|
'read:roles:services': {'description': 'Read service role assignments.'},
|
||||||
|
'read:roles:groups': {'description': 'Read group role assignments.'},
|
||||||
'users:activity': {
|
'users:activity': {
|
||||||
'description': 'Update time of last user activity.',
|
'description': 'Update time of last user activity.',
|
||||||
'subscopes': ['read:users:activity'],
|
'subscopes': ['read:users:activity'],
|
||||||
},
|
},
|
||||||
'admin:users:servers': {
|
'admin:servers': {
|
||||||
'description': 'Read, start, stop, create and delete user servers and their state.',
|
'description': 'Read, start, stop, create and delete user servers and their state.',
|
||||||
'subscopes': ['admin:users:server_state', 'users:servers'],
|
'subscopes': ['admin:server_state', 'servers'],
|
||||||
},
|
},
|
||||||
'admin:users:server_state': {'description': 'Read and write users’ server state.'},
|
'admin:server_state': {'description': 'Read and write users’ server state.'},
|
||||||
'users:servers': {
|
'servers': {
|
||||||
'description': 'Start and stop user servers.',
|
'description': 'Start and stop user servers.',
|
||||||
'subscopes': ['read:users:servers'],
|
'subscopes': ['read:servers'],
|
||||||
},
|
},
|
||||||
'read:users:servers': {
|
'read:servers': {
|
||||||
'description': 'Read users’ names and their server models (excluding the server state).',
|
'description': 'Read users’ names and their server models (excluding the server state).',
|
||||||
'subscopes': ['read:users:name'],
|
'subscopes': ['read:users:name'],
|
||||||
},
|
},
|
||||||
'users:tokens': {
|
'tokens': {
|
||||||
'description': 'Read, write, create and delete user tokens.',
|
'description': 'Read, write, create and delete user tokens.',
|
||||||
'subscopes': ['read:users:tokens'],
|
'subscopes': ['read:tokens'],
|
||||||
},
|
},
|
||||||
'read:users:tokens': {'description': 'Read user tokens.'},
|
'read:tokens': {'description': 'Read user tokens.'},
|
||||||
'admin:groups': {
|
'admin:groups': {
|
||||||
'description': 'Read and write group information, create and delete groups.',
|
'description': 'Read and write group information, create and delete groups.',
|
||||||
'subscopes': ['groups', 'read:groups:roles'],
|
'subscopes': ['groups', 'read:roles:groups'],
|
||||||
},
|
},
|
||||||
'groups': {
|
'groups': {
|
||||||
'description': 'Read and write group information, including adding/removing users to/from groups.',
|
'description': 'Read and write group information, including adding/removing users to/from groups.',
|
||||||
@@ -88,15 +94,13 @@ scope_definitions = {
|
|||||||
'subscopes': ['read:groups:name'],
|
'subscopes': ['read:groups:name'],
|
||||||
},
|
},
|
||||||
'read:groups:name': {'description': 'Read group names.'},
|
'read:groups:name': {'description': 'Read group names.'},
|
||||||
'read:groups:roles': {'description': 'Read group role assignments.'},
|
|
||||||
'read:services': {
|
'read:services': {
|
||||||
'description': 'Read service models.',
|
'description': 'Read service models.',
|
||||||
'subscopes': ['read:services:name'],
|
'subscopes': ['read:services:name'],
|
||||||
},
|
},
|
||||||
'read:services:name': {'description': 'Read service names.'},
|
'read:services:name': {'description': 'Read service names.'},
|
||||||
'read:services:roles': {'description': 'Read service role assignments.'},
|
|
||||||
'read:hub': {'description': 'Read detailed information about the Hub.'},
|
'read:hub': {'description': 'Read detailed information about the Hub.'},
|
||||||
'access:users:servers': {
|
'access:servers': {
|
||||||
'description': 'Access user servers via API or browser.',
|
'description': 'Access user servers via API or browser.',
|
||||||
},
|
},
|
||||||
'access:services': {
|
'access:services': {
|
||||||
@@ -279,7 +283,7 @@ def get_scopes_for(orm_object):
|
|||||||
spawner = orm_object.oauth_client.spawner
|
spawner = orm_object.oauth_client.spawner
|
||||||
if spawner:
|
if spawner:
|
||||||
token_scopes.add(
|
token_scopes.add(
|
||||||
f"access:users:servers!server={spawner.user.name}/{spawner.name}"
|
f"access:servers!server={spawner.user.name}/{spawner.name}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
service = orm_object.oauth_client.service
|
service = orm_object.oauth_client.service
|
||||||
@@ -388,7 +392,7 @@ def parse_scopes(scope_list):
|
|||||||
"""
|
"""
|
||||||
Parses scopes and filters in something akin to JSON style
|
Parses scopes and filters in something akin to JSON style
|
||||||
|
|
||||||
For instance, scope list ["users", "groups!group=foo", "users:servers!server=user/bar", "users:servers!server=user/baz"]
|
For instance, scope list ["users", "groups!group=foo", "servers!server=user/bar", "servers!server=user/baz"]
|
||||||
would lead to scope model
|
would lead to scope model
|
||||||
{
|
{
|
||||||
"users":scope.ALL,
|
"users":scope.ALL,
|
||||||
@@ -397,7 +401,7 @@ def parse_scopes(scope_list):
|
|||||||
"alice"
|
"alice"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"users:servers":{
|
"servers":{
|
||||||
"server":[
|
"server":[
|
||||||
"user/bar",
|
"user/bar",
|
||||||
"user/baz"
|
"user/baz"
|
||||||
|
@@ -835,8 +835,8 @@ class HubAuthenticated:
|
|||||||
which in turn is set by $JUPYTERHUB_OAUTH_SCOPES
|
which in turn is set by $JUPYTERHUB_OAUTH_SCOPES
|
||||||
Default values include:
|
Default values include:
|
||||||
- 'access:services', 'access:services!service={service_name}' for services
|
- 'access:services', 'access:services!service={service_name}' for services
|
||||||
- 'access:users:servers', 'access:users:servers!user={user}',
|
- 'access:servers', 'access:servers!user={user}',
|
||||||
'access:users:servers!server={user}/{server_name}'
|
'access:servers!server={user}/{server_name}'
|
||||||
for single-user servers
|
for single-user servers
|
||||||
|
|
||||||
If hub_scopes is not used (e.g. JupyterHub 1.x),
|
If hub_scopes is not used (e.g. JupyterHub 1.x),
|
||||||
|
@@ -222,8 +222,8 @@ class Spawner(LoggingConfigurable):
|
|||||||
@default("oauth_scopes")
|
@default("oauth_scopes")
|
||||||
def _default_oauth_scopes(self):
|
def _default_oauth_scopes(self):
|
||||||
return [
|
return [
|
||||||
f"access:users:servers!server={self.user.name}/{self.name}",
|
f"access:servers!server={self.user.name}/{self.name}",
|
||||||
f"access:users:servers!user={self.user.name}",
|
f"access:servers!user={self.user.name}",
|
||||||
]
|
]
|
||||||
|
|
||||||
handler = Any()
|
handler = Any()
|
||||||
|
@@ -956,7 +956,7 @@ async def test_oauth_page_scope_appearance(
|
|||||||
[
|
[
|
||||||
'self',
|
'self',
|
||||||
'read:users!user=gawain',
|
'read:users!user=gawain',
|
||||||
'read:users:tokens',
|
'read:tokens',
|
||||||
'read:groups!group=mythos',
|
'read:groups!group=mythos',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@@ -180,13 +180,13 @@ def test_orm_roles_delete_cascade(db):
|
|||||||
['admin:users'],
|
['admin:users'],
|
||||||
{
|
{
|
||||||
'admin:users',
|
'admin:users',
|
||||||
'admin:users:auth_state',
|
'admin:auth_state',
|
||||||
'users',
|
'users',
|
||||||
'read:users',
|
'read:users',
|
||||||
'users:activity',
|
'users:activity',
|
||||||
'read:users:name',
|
'read:users:name',
|
||||||
'read:users:groups',
|
'read:users:groups',
|
||||||
'read:users:roles',
|
'read:roles:users',
|
||||||
'read:users:activity',
|
'read:users:activity',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -210,32 +210,32 @@ def test_orm_roles_delete_cascade(db):
|
|||||||
'read:users:activity',
|
'read:users:activity',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(['read:users:servers'], {'read:users:servers', 'read:users:name'}),
|
(['read:servers'], {'read:servers', 'read:users:name'}),
|
||||||
(
|
(
|
||||||
['admin:groups'],
|
['admin:groups'],
|
||||||
{
|
{
|
||||||
'admin:groups',
|
'admin:groups',
|
||||||
'groups',
|
'groups',
|
||||||
'read:groups',
|
'read:groups',
|
||||||
'read:groups:roles',
|
'read:roles:groups',
|
||||||
'read:groups:name',
|
'read:groups:name',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['admin:groups', 'read:users:servers'],
|
['admin:groups', 'read:servers'],
|
||||||
{
|
{
|
||||||
'admin:groups',
|
'admin:groups',
|
||||||
'groups',
|
'groups',
|
||||||
'read:groups',
|
'read:groups',
|
||||||
'read:groups:roles',
|
'read:roles:groups',
|
||||||
'read:groups:name',
|
'read:groups:name',
|
||||||
'read:users:servers',
|
'read:servers',
|
||||||
'read:users:name',
|
'read:users:name',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['users:tokens!group=hobbits'],
|
['tokens!group=hobbits'],
|
||||||
{'users:tokens!group=hobbits', 'read:users:tokens!group=hobbits'},
|
{'tokens!group=hobbits', 'read:tokens!group=hobbits'},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -398,7 +398,7 @@ async def test_delete_roles(db, role_type, rolename, response_type, response):
|
|||||||
'users',
|
'users',
|
||||||
'users!user=charlie',
|
'users!user=charlie',
|
||||||
'admin:groups',
|
'admin:groups',
|
||||||
'read:users:tokens',
|
'read:tokens',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'existing',
|
'existing',
|
||||||
@@ -501,8 +501,8 @@ async def test_load_roles_services(tmpdir, request):
|
|||||||
'scopes': [
|
'scopes': [
|
||||||
'read:users:name',
|
'read:users:name',
|
||||||
'read:users:activity',
|
'read:users:activity',
|
||||||
'read:users:servers',
|
'read:servers',
|
||||||
'users:servers',
|
'servers',
|
||||||
],
|
],
|
||||||
'services': ['idle-culler'],
|
'services': ['idle-culler'],
|
||||||
},
|
},
|
||||||
|
@@ -79,9 +79,9 @@ def test_scope_multiple_filters():
|
|||||||
|
|
||||||
def test_scope_parse_server_name():
|
def test_scope_parse_server_name():
|
||||||
handler = get_handler_with_scopes(
|
handler = get_handler_with_scopes(
|
||||||
['users:servers!server=maeby/server1', 'read:users!user=maeby']
|
['servers!server=maeby/server1', 'read:users!user=maeby']
|
||||||
)
|
)
|
||||||
assert _check_scope_access(handler, 'users:servers', user='maeby', server='server1')
|
assert _check_scope_access(handler, 'servers', user='maeby', server='server1')
|
||||||
|
|
||||||
|
|
||||||
class MockAPIHandler:
|
class MockAPIHandler:
|
||||||
@@ -99,7 +99,7 @@ class MockAPIHandler:
|
|||||||
def user_thing(self, user_name):
|
def user_thing(self, user_name):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@needs_scope('users:servers')
|
@needs_scope('servers')
|
||||||
def server_thing(self, user_name, server_name):
|
def server_thing(self, user_name, server_name):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -140,18 +140,18 @@ def mock_handler():
|
|||||||
(['users!user=george'], 'user_thing', ('fake_user',), False),
|
(['users!user=george'], 'user_thing', ('fake_user',), False),
|
||||||
(['users!user=george'], 'user_thing', ('oscar',), False),
|
(['users!user=george'], 'user_thing', ('oscar',), False),
|
||||||
(['users!user=george', 'users!user=oscar'], 'user_thing', ('oscar',), True),
|
(['users!user=george', 'users!user=oscar'], 'user_thing', ('oscar',), True),
|
||||||
(['users:servers'], 'server_thing', ('user1', 'server_1'), True),
|
(['servers'], 'server_thing', ('user1', 'server_1'), True),
|
||||||
(['users:servers'], 'server_thing', ('user1', ''), True),
|
(['servers'], 'server_thing', ('user1', ''), True),
|
||||||
(['users:servers'], 'server_thing', ('user1', None), True),
|
(['servers'], 'server_thing', ('user1', None), True),
|
||||||
(
|
(
|
||||||
['users:servers!server=maeby/bluth'],
|
['servers!server=maeby/bluth'],
|
||||||
'server_thing',
|
'server_thing',
|
||||||
('maeby', 'bluth'),
|
('maeby', 'bluth'),
|
||||||
True,
|
True,
|
||||||
),
|
),
|
||||||
(['users:servers!server=maeby/bluth'], 'server_thing', ('gob', 'bluth'), False),
|
(['servers!server=maeby/bluth'], 'server_thing', ('gob', 'bluth'), False),
|
||||||
(
|
(
|
||||||
['users:servers!server=maeby/bluth'],
|
['servers!server=maeby/bluth'],
|
||||||
'server_thing',
|
'server_thing',
|
||||||
('maybe', 'bluth2'),
|
('maybe', 'bluth2'),
|
||||||
False,
|
False,
|
||||||
@@ -488,17 +488,17 @@ async def test_metascope_all_expansion(app, create_user_with_scopes):
|
|||||||
@mark.parametrize(
|
@mark.parametrize(
|
||||||
"scopes, can_stop ,num_servers, keys_in, keys_out",
|
"scopes, can_stop ,num_servers, keys_in, keys_out",
|
||||||
[
|
[
|
||||||
(['read:users:servers!user=almond'], False, 2, {'name'}, {'state'}),
|
(['read:servers!user=almond'], False, 2, {'name'}, {'state'}),
|
||||||
(['admin:users', 'read:users'], False, 0, set(), set()),
|
(['admin:users', 'read:users'], False, 0, set(), set()),
|
||||||
(
|
(
|
||||||
['read:users:servers!group=nuts', 'users:servers'],
|
['read:servers!group=nuts', 'servers'],
|
||||||
True,
|
True,
|
||||||
2,
|
2,
|
||||||
{'name'},
|
{'name'},
|
||||||
{'state'},
|
{'state'},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['admin:users:server_state', 'read:users:servers'],
|
['admin:server_state', 'read:servers'],
|
||||||
False,
|
False,
|
||||||
2,
|
2,
|
||||||
{'name', 'state'},
|
{'name', 'state'},
|
||||||
@@ -506,8 +506,8 @@ async def test_metascope_all_expansion(app, create_user_with_scopes):
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
[
|
[
|
||||||
'read:users:servers!server=almond/bianca',
|
'read:servers!server=almond/bianca',
|
||||||
'admin:users:server_state!server=almond/bianca',
|
'admin:server_state!server=almond/bianca',
|
||||||
],
|
],
|
||||||
False,
|
False,
|
||||||
1,
|
1,
|
||||||
@@ -634,8 +634,8 @@ async def test_server_state_access(
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
'no_intersection_user_server',
|
'no_intersection_user_server',
|
||||||
['users:servers!user=y'],
|
['servers!user=y'],
|
||||||
['users:servers!server=x'],
|
['servers!server=x'],
|
||||||
set(),
|
set(),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -704,7 +704,7 @@ async def test_resolve_token_permissions(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{'read:services:roles', 'read:services:name'},
|
{'read:roles:services', 'read:services:name'},
|
||||||
{'name', 'kind', 'roles', 'admin'},
|
{'name', 'kind', 'roles', 'admin'},
|
||||||
),
|
),
|
||||||
({'read:services:name'}, {'name', 'kind', 'admin'}),
|
({'read:services:name'}, {'name', 'kind', 'admin'}),
|
||||||
@@ -734,7 +734,7 @@ async def test_service_model_filtering(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{'read:groups:roles', 'read:groups:name'},
|
{'read:roles:groups', 'read:groups:name'},
|
||||||
{'name', 'kind', 'roles'},
|
{'name', 'kind', 'roles'},
|
||||||
),
|
),
|
||||||
({'read:groups:name'}, {'name', 'kind'}),
|
({'read:groups:name'}, {'name', 'kind'}),
|
||||||
@@ -761,7 +761,7 @@ async def test_group_model_filtering(
|
|||||||
|
|
||||||
async def test_roles_access(app, create_service_with_scopes, create_user_with_scopes):
|
async def test_roles_access(app, create_service_with_scopes, create_user_with_scopes):
|
||||||
user = add_user(app.db, name='miranda')
|
user = add_user(app.db, name='miranda')
|
||||||
read_user = create_user_with_scopes('read:users:roles')
|
read_user = create_user_with_scopes('read:roles:users')
|
||||||
r = await api_request(
|
r = await api_request(
|
||||||
app, 'users', user.name, headers=auth_header(app.db, read_user.name)
|
app, 'users', user.name, headers=auth_header(app.db, read_user.name)
|
||||||
)
|
)
|
||||||
@@ -822,15 +822,15 @@ async def test_roles_access(app, create_service_with_scopes, create_user_with_sc
|
|||||||
),
|
),
|
||||||
# resolves server under user, without warning
|
# resolves server under user, without warning
|
||||||
(
|
(
|
||||||
set(["read:users:servers!user=abc"]),
|
set(["read:servers!user=abc"]),
|
||||||
set(["read:users:servers!server=abc/xyz"]),
|
set(["read:servers!server=abc/xyz"]),
|
||||||
set(["read:users:servers!server=abc/xyz"]),
|
set(["read:servers!server=abc/xyz"]),
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
# user->server, no match
|
# user->server, no match
|
||||||
(
|
(
|
||||||
set(["read:users:servers!user=abc"]),
|
set(["read:servers!user=abc"]),
|
||||||
set(["read:users:servers!server=abcd/xyz"]),
|
set(["read:servers!server=abcd/xyz"]),
|
||||||
set([]),
|
set([]),
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
|
@@ -128,7 +128,7 @@ async def test_hubauth_token(app, mockservice_url, create_user_with_scopes):
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
[
|
[
|
||||||
"access:users:servers!user=$service",
|
"access:servers!user=$service",
|
||||||
],
|
],
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
|
@@ -17,18 +17,18 @@ from .utils import AsyncSession
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"access_scopes, server_name, expect_success",
|
"access_scopes, server_name, expect_success",
|
||||||
[
|
[
|
||||||
(["access:users:servers!group=$group"], "", True),
|
(["access:servers!group=$group"], "", True),
|
||||||
(["access:users:servers!group=other-group"], "", False),
|
(["access:servers!group=other-group"], "", False),
|
||||||
(["access:users:servers"], "", True),
|
(["access:servers"], "", True),
|
||||||
(["access:users:servers"], "named", True),
|
(["access:servers"], "named", True),
|
||||||
(["access:users:servers!user=$user"], "", True),
|
(["access:servers!user=$user"], "", True),
|
||||||
(["access:users:servers!user=$user"], "named", True),
|
(["access:servers!user=$user"], "named", True),
|
||||||
(["access:users:servers!server=$server"], "", True),
|
(["access:servers!server=$server"], "", True),
|
||||||
(["access:users:servers!server=$server"], "named-server", True),
|
(["access:servers!server=$server"], "named-server", True),
|
||||||
(["access:users:servers!server=$user/other"], "", False),
|
(["access:servers!server=$user/other"], "", False),
|
||||||
(["access:users:servers!server=$user/other"], "some-name", False),
|
(["access:servers!server=$user/other"], "some-name", False),
|
||||||
(["access:users:servers!user=$other"], "", False),
|
(["access:servers!user=$other"], "", False),
|
||||||
(["access:users:servers!user=$other"], "named", False),
|
(["access:servers!user=$other"], "named", False),
|
||||||
(["access:services"], "", False),
|
(["access:services"], "", False),
|
||||||
(["self"], "named", False),
|
(["self"], "named", False),
|
||||||
([], "", False),
|
([], "", False),
|
||||||
|
Reference in New Issue
Block a user