33from logging import Logger
44from urllib .parse import urlparse
55
6+ from jupyterhub import __version__ as __jh_version__
7+ from jupyterhub .services .auth import HubOAuthCallbackHandler , HubOAuthenticated
8+ from jupyterhub .utils import make_ssl_context
9+ from tornado import httpclient , web
610from tornado .log import app_log
711from tornado .web import Application
812from tornado .websocket import WebSocketHandler
913
1014from ..handlers import SuperviseAndProxyHandler
1115
1216
13- class StandaloneProxyHandler ( SuperviseAndProxyHandler ):
17+ class StandaloneHubProxyHandler ( HubOAuthenticated , SuperviseAndProxyHandler ):
1418 """
15- Base class for standalone proxies. Will not ensure any authentication!
19+ Base class for standalone proxies.
20+ Will restrict access to the application by authentication with the JupyterHub API.
1621 """
1722
1823 def __init__ (self , * args , ** kwargs ):
1924 super ().__init__ (* args , ** kwargs )
2025 self .environment = {}
2126 self .timeout = 60
27+ self .skip_authentication = False
2228
2329 @property
2430 def log (self ) -> Logger :
2531 return app_log
2632
33+ @property
34+ def hub_users (self ):
35+ if "hub_user" in self .settings :
36+ return {self .settings ["hub_user" ]}
37+ return set ()
38+
39+ @property
40+ def hub_groups (self ):
41+ if "hub_group" in self .settings :
42+ return {self .settings ["hub_group" ]}
43+ return set ()
44+
45+ def set_default_headers (self ):
46+ self .set_header ("X-JupyterHub-Version" , __jh_version__ )
47+
2748 def prepare (self , * args , ** kwargs ):
2849 pass
2950
51+ async def proxy (self , port , path ):
52+ if self .skip_authentication :
53+ return await super ().proxy (port , path )
54+ else :
55+ return await self .oauth_proxy (port , path )
56+
57+ @web .authenticated
58+ async def oauth_proxy (self , port , path ):
59+ return await super ().proxy (port , path )
60+
3061 def check_origin (self , origin : str = None ):
3162 # Skip JupyterHandler.check_origin
3263 return WebSocketHandler .check_origin (self , origin )
@@ -38,6 +69,23 @@ def get_timeout(self):
3869 return self .timeout
3970
4071
72+ def configure_ssl ():
73+ keyfile = os .environ .get ("JUPYTERHUB_SSL_KEYFILE" )
74+ certfile = os .environ .get ("JUPYTERHUB_SSL_CERTFILE" )
75+ cafile = os .environ .get ("JUPYTERHUB_SSL_CLIENT_CA" )
76+
77+ if not (keyfile and certfile and cafile ):
78+ app_log .warn ("Could not configure SSL" )
79+ return None
80+
81+ ssl_context = make_ssl_context (keyfile , certfile , cafile )
82+
83+ # Configure HTTPClient to use SSL for Proxy Requests
84+ httpclient .AsyncHTTPClient .configure (None , defaults = {"ssl_options" : ssl_context })
85+
86+ return ssl_context
87+
88+
4189def make_proxy_app (
4290 command : list [str ],
4391 prefix : str ,
@@ -46,25 +94,17 @@ def make_proxy_app(
4694 environment : dict [str , str ],
4795 mappath : dict [str , str ],
4896 timeout : int ,
49- use_jupyterhub : bool ,
97+ skip_authentication : bool ,
5098 debug : bool ,
5199 # progressive: bool,
52100 websocket_max_message_size : int ,
53101):
54- # Determine base class, whether or not to authenticate with JupyterHub
55- if use_jupyterhub :
56- from .hub import StandaloneHubProxyHandler
57-
58- proxy_base = StandaloneHubProxyHandler
59- else :
60- proxy_base = StandaloneProxyHandler
61-
62102 app_log .debug (f"Process will use { port = } " )
63103 app_log .debug (f"Process will use { unix_socket = } " )
64104 app_log .debug (f"Process environment: { environment } " )
65105 app_log .debug (f"Proxy mappath: { mappath } " )
66106
67- class Proxy (proxy_base ):
107+ class Proxy (StandaloneHubProxyHandler ):
68108 def __init__ (self , * args , ** kwargs ):
69109 super ().__init__ (* args , ** kwargs )
70110 self .name = f"{ command [0 ]!r} Process"
@@ -75,6 +115,7 @@ def __init__(self, *args, **kwargs):
75115 self .command = command
76116 self .environment = environment
77117 self .timeout = timeout
118+ self .skip_authentication = skip_authentication
78119
79120 settings = dict (
80121 debug = debug ,
@@ -97,25 +138,15 @@ def __init__(self, *args, **kwargs):
97138 state = {},
98139 # ToDo: progressive=progressive
99140 ),
100- )
141+ ),
142+ (
143+ r"^" + re .escape (prefix ) + r"/oauth_callback" ,
144+ HubOAuthCallbackHandler ,
145+ ),
101146 ],
102147 ** settings ,
103148 )
104149
105- if use_jupyterhub :
106- from jupyterhub .services .auth import HubOAuthCallbackHandler
107-
108- # The OAuth Callback required to redirect when we successfully authenticated with JupyterHub
109- app .add_handlers (
110- ".*" ,
111- [
112- (
113- r"^" + re .escape (prefix ) + r"/oauth_callback" ,
114- HubOAuthCallbackHandler ,
115- ),
116- ],
117- )
118-
119150 return app
120151
121152
0 commit comments