mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-12 20:43:02 +00:00
allow custom scopes
defined with c.JupyterHub.custom_scopes = { 'custom:scope': {'description': "text shown on oauth confirm"} } Allows injecting custom scopes to roles, allowing extension of granular permissions to service-defined custom scopes. Custom scopes: - MUST start with `custom:` - MUST only contain ascii lowercase, numbers, colon, hyphen, asterisk, underscore - MUST define a `description` - MAY also define `subscopes` list(s), each of which must also be explicitly defined HubAuth can be used to retrieve and check for custom scopes to authorize requests.
This commit is contained in:
116
examples/custom-scopes/grades.py
Normal file
116
examples/custom-scopes/grades.py
Normal file
@@ -0,0 +1,116 @@
|
||||
import os
|
||||
from functools import wraps
|
||||
from html import escape
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado.web import Application
|
||||
from tornado.web import authenticated
|
||||
from tornado.web import RequestHandler
|
||||
|
||||
from jupyterhub.services.auth import HubOAuthCallbackHandler
|
||||
from jupyterhub.services.auth import HubOAuthenticated
|
||||
from jupyterhub.utils import url_path_join
|
||||
|
||||
SCOPE_PREFIX = "custom:grades"
|
||||
READ_SCOPE = f"{SCOPE_PREFIX}:read"
|
||||
WRITE_SCOPE = f"{SCOPE_PREFIX}:write"
|
||||
|
||||
|
||||
def require_scope(scopes):
|
||||
"""Decorator to require scopes
|
||||
|
||||
For use if multiple methods on one Handler
|
||||
may want different scopes,
|
||||
so class-level .hub_scopes is insufficient
|
||||
(e.g. read for GET, write for POST).
|
||||
"""
|
||||
if isinstance(scopes, str):
|
||||
scopes = [scopes]
|
||||
|
||||
def wrap(method):
|
||||
"""The actual decorator"""
|
||||
|
||||
@wraps(method)
|
||||
@authenticated
|
||||
def wrapped(self, *args, **kwargs):
|
||||
self.hub_scopes = scopes
|
||||
return method(self, *args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
class GradesHandler(HubOAuthenticated, RequestHandler):
|
||||
# default scope for this Handler: read-only
|
||||
hub_scopes = [READ_SCOPE]
|
||||
|
||||
def _render(self):
|
||||
grades = self.settings["grades"]
|
||||
self.write("<h1>Grades</h1>")
|
||||
self.write("<table>")
|
||||
self.write("<tr><th>Student</th><th>Grade</th></tr>")
|
||||
for student, grade in grades.items():
|
||||
qstudent = escape(student)
|
||||
qgrade = escape(str(grade))
|
||||
self.write(
|
||||
f"""
|
||||
<tr>
|
||||
<td class="student">{qstudent}</td>
|
||||
<td class="grade">{qgrade}</td>
|
||||
</tr>
|
||||
"""
|
||||
)
|
||||
if WRITE_SCOPE in self.current_user["scopes"]:
|
||||
self.write("Enter grade:")
|
||||
self.write(
|
||||
"""
|
||||
<form action=. method=POST>
|
||||
<input name=student placeholder=student></input>
|
||||
<input kind=number name=grade placeholder=grade></input>
|
||||
<input type="submit" value="Submit">
|
||||
"""
|
||||
)
|
||||
|
||||
@require_scope([READ_SCOPE])
|
||||
async def get(self):
|
||||
self._render()
|
||||
|
||||
# POST requires WRITE_SCOPE instead of READ_SCOPE
|
||||
@require_scope([WRITE_SCOPE])
|
||||
async def post(self):
|
||||
name = self.get_argument("student")
|
||||
grade = self.get_argument("grade")
|
||||
self.settings["grades"][name] = grade
|
||||
self._render()
|
||||
|
||||
|
||||
def main():
|
||||
base_url = os.environ['JUPYTERHUB_SERVICE_PREFIX']
|
||||
|
||||
app = Application(
|
||||
[
|
||||
(base_url, GradesHandler),
|
||||
(
|
||||
url_path_join(base_url, 'oauth_callback'),
|
||||
HubOAuthCallbackHandler,
|
||||
),
|
||||
],
|
||||
cookie_secret=os.urandom(32),
|
||||
grades={"student": 53},
|
||||
)
|
||||
|
||||
http_server = HTTPServer(app)
|
||||
url = urlparse(os.environ['JUPYTERHUB_SERVICE_URL'])
|
||||
|
||||
http_server.listen(url.port, url.hostname)
|
||||
try:
|
||||
IOLoop.current().start()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Reference in New Issue
Block a user