Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jupyter server 2.0 compatability fixes #649

Merged
merged 3 commits into from
Jan 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 42 additions & 22 deletions nbdime/tests/test_cli_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ def test_git_mergedriver(git_repo, filespath):


@pytest.mark.timeout(timeout=3*WEB_TEST_TIMEOUT)
def test_git_difftool(git_repo, unique_port, popen_with_terminator, auth_header):
def test_git_difftool(git_repo, unique_port, popen_with_terminator):
gitdifftool.main(['config', '--enable'])
cmd = get_output('git config --get --local difftool.nbdime.cmd').strip()

Expand All @@ -605,10 +605,14 @@ def test_git_difftool(git_repo, unique_port, popen_with_terminator, auth_header)
for _ in range(3):
wait_up(url, check=lambda: process.poll() is None)
# server started
r = requests.get(url + '/difftool')
s = requests.Session()
r = s.get(url + '/difftool')
r.raise_for_status()
# close it
r = requests.post(url + '/api/closetool', json={'exitCode': 0}, headers=auth_header)
r = s.post(url + '/api/closetool', data={
'exitCode': 0,
'_xsrf': r.cookies['_xsrf']
})
r.raise_for_status()
time.sleep(0.25)
# wait for exit
Expand All @@ -617,7 +621,7 @@ def test_git_difftool(git_repo, unique_port, popen_with_terminator, auth_header)


@pytest.mark.timeout(timeout=3*WEB_TEST_TIMEOUT)
def test_git_mergetool(git_repo, unique_port, popen_with_terminator, auth_header):
def test_git_mergetool(git_repo, unique_port, popen_with_terminator):
gitmergetool.main(['config', '--enable'])
cmd = get_output('git config --get --local mergetool.nbdime.cmd').strip()

Expand All @@ -636,18 +640,24 @@ def test_git_mergetool(git_repo, unique_port, popen_with_terminator, auth_header
url = 'http://127.0.0.1:%i' % port
wait_up(url, check=lambda: process.poll() is None)
# server started
r = requests.get(url + '/mergetool')
s = requests.Session()
r = s.get(url + '/mergetool')
r.raise_for_status()
r = requests.post(
xsrf = r.cookies['_xsrf']
r = s.post(
url_concat(url + '/api/store', {'outputfilename': 'merge-conflict.ipynb'}),
data=json.dumps({
'merged': nbformat.v4.new_notebook(),
}),
headers=auth_header
json={'merged': nbformat.v4.new_notebook()},
headers={'X-XSRFToken': xsrf},
)
r.raise_for_status()
# close it
r = requests.post(url + '/api/closetool', json={'exitCode': 0}, headers=auth_header)
r = s.post(
url + '/api/closetool',
data={
'exitCode': 0,
'_xsrf': xsrf,
}
)
r.raise_for_status()
# wait for exit
process.wait()
Expand Down Expand Up @@ -694,7 +704,7 @@ def test_hg_mergedriver(hg_repo, filespath, reset_log):


@pytest.mark.timeout(timeout=3*WEB_TEST_TIMEOUT)
def test_hg_diffweb(hg_repo, unique_port, popen_with_terminator, auth_header):
def test_hg_diffweb(hg_repo, unique_port, popen_with_terminator):
# enable diff/merge drivers
write_local_hg_config(hg_repo)

Expand All @@ -706,10 +716,14 @@ def test_hg_diffweb(hg_repo, unique_port, popen_with_terminator, auth_header):
wait_up(url, check=lambda: process.poll() is None)
for _ in range(3):
# server started
r = requests.get(url + '/difftool')
s = requests.Session()
r = s.get(url + '/difftool')
r.raise_for_status()
# close it
r = requests.post(url + '/api/closetool', json={'exitCode': 0}, headers=auth_header)
r = s.post(url + '/api/closetool', data={
'exitCode': 0,
'_xsrf': r.cookies['_xsrf']
})
r.raise_for_status()
time.sleep(0.25)
# wait for exit
Expand All @@ -718,7 +732,7 @@ def test_hg_diffweb(hg_repo, unique_port, popen_with_terminator, auth_header):


@pytest.mark.timeout(timeout=WEB_TEST_TIMEOUT)
def test_hg_mergetool(hg_repo, unique_port, popen_with_terminator, auth_header):
def test_hg_mergetool(hg_repo, unique_port, popen_with_terminator):
# enable diff/merge drivers
write_local_hg_config(hg_repo)

Expand All @@ -733,18 +747,24 @@ def test_hg_mergetool(hg_repo, unique_port, popen_with_terminator, auth_header):
url = 'http://127.0.0.1:%i' % unique_port
wait_up(url, check=lambda: process.poll() is None)
# server started
r = requests.get(url + '/mergetool')
s = requests.Session()
r = s.get(url + '/mergetool')
r.raise_for_status()
r = requests.post(
xsrf = r.cookies['_xsrf']
r = s.post(
url_concat(url + '/api/store', {'outputfilename': 'merge-conflict.ipynb'}),
data=json.dumps({
'merged': nbformat.v4.new_notebook(),
}),
headers=auth_header
json={'merged': nbformat.v4.new_notebook()},
headers={'X-XSRFToken': xsrf},
)
r.raise_for_status()
# close it
r = requests.post(url + '/api/closetool', json={'exitCode': 0}, headers=auth_header)
r = s.post(
url + '/api/closetool',
data={
'exitCode': 0,
'_xsrf': xsrf
}
)
r.raise_for_status()
# wait for exit
process.wait()
Expand Down
18 changes: 12 additions & 6 deletions nbdime/tests/test_server_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
def test_isgit(git_repo2, server_extension_app):
url = 'http://127.0.0.1:%i/nbdime/api/isgit' % server_extension_app['port']
r = requests.post(
url, headers=auth_header,
url,
headers=auth_header,
data=json.dumps({
'path': git_repo2,
}))
Expand All @@ -45,7 +46,8 @@ def test_isgit(git_repo2, server_extension_app):
def test_isgit_nonrepo(git_repo2, server_extension_app):
url = 'http://127.0.0.1:%i/nbdime/api/isgit' % server_extension_app['port']
r = requests.post(
url, headers=auth_header,
url,
headers=auth_header,
data=json.dumps({
'path': server_extension_app['path'],
}))
Expand All @@ -64,7 +66,8 @@ def test_difftool(git_repo2, server_extension_app):
def test_git_difftool(git_repo2, server_extension_app):
url = 'http://127.0.0.1:%i/nbdime/git-difftool' % server_extension_app['port']
r = requests.get(
url, headers=auth_header)
url,
headers=auth_header)
r.raise_for_status()
assert r.text.startswith('<!DOCTYPE html')
# Extract config data
Expand All @@ -87,7 +90,8 @@ def test_diff_api(git_repo2, server_extension_app):
local_path = os.path.relpath(git_repo2, server_extension_app['path'])
url = 'http://127.0.0.1:%i/nbdime/api/diff' % server_extension_app['port']
r = requests.post(
url, headers=auth_header,
url,
headers=auth_header,
data=json.dumps({
'base': 'git:' + pjoin(local_path, 'diff.ipynb'),
}))
Expand Down Expand Up @@ -124,7 +128,8 @@ def _make_ref(key):
):
print(args)
r = requests.post(
url, headers=auth_header,
url,
headers=auth_header,
data=json.dumps({
'ref_local': _make_ref(args[0]),
'ref_remote': _make_ref(args[1]),
Expand Down Expand Up @@ -193,7 +198,8 @@ def test_diff_api_symlink(git_repo2, server_extension_app, needs_symlink):
local_path = os.path.relpath(symlink, server_extension_app['path'])
url = 'http://127.0.0.1:%i/nbdime/api/diff' % server_extension_app['port']
r = requests.post(
url, headers=auth_header,
url,
headers=auth_header,
data=json.dumps({
'base': 'git:' + pjoin(local_path, 'diff.ipynb'),
}))
Expand Down
22 changes: 17 additions & 5 deletions nbdime/webapp/nbdimeserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,17 @@ def post(self):
# Fail if no exit code is supplied:
fallback = int(self.request.headers.get('exit_code', 1))
try:
self.application.exit_code = json.loads(self.request.body).get('exitCode', fallback)
except json.JSONDecodeError:
self.application.exit_code = fallback

_logger.info('Closing server on remote request')
self.application.exit_code = self.get_argument('exitCode')
except web.MissingArgumentError:
try:
self.application.exit_code = json.loads(self.request.body).get('exitCode', fallback)
except json.JSONDecodeError:
self.application.exit_code = fallback

if isinstance(self.application.exit_code, str):
self.application.exit_code = int(self.application.exit_code, 10)

_logger.info('Closing server on remote request (%d)', self.application.exit_code)
self.finish()
ioloop.IOLoop.current().stop()

Expand Down Expand Up @@ -387,6 +393,12 @@ def make_app(**params):
'cookie_secret': base64.encodebytes(os.urandom(32)), # Needed even for an unsecured server.
}

try:
from jupyter_server.auth import IdentityProvider
settings['identity_provider'] = IdentityProvider()
except ImportError:
pass

if is_in_repo(nbdime_root):
# don't cache when working from repo
settings.update({
Expand Down
35 changes: 34 additions & 1 deletion packages/webapp/src/app/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import {
URLExt
} from '@jupyterlab/coreutils/lib/url';

import type {
PartialJSONObject
} from '@lumino/coreutils';

import type {
Widget
} from '@lumino/widgets';
Expand Down Expand Up @@ -209,6 +213,35 @@ function markUnchangedRanges() {
}


/**
* Get the XSRF token from the cookie, if present
*/
function getXsrfToken(): string | undefined {
const r = document.cookie.match(/\b_xsrf=([^;]*)\b/);
return r ? r[1] : undefined;
}


/**
* Wrap a navigator.sendBeacon call with XSRF data
*/
function sendBeacon(url: string, data: PartialJSONObject): void {
const formData = new FormData();
const token = getXsrfToken();
if (token) {
formData.append('_xsrf', token);
}

for (let key of Object.keys(data)) {
if (data[key]) {
formData.append(key, data[key]!.toString() );
}
}

navigator.sendBeacon(url, formData);
}


export let toolClosed = false;
/**
* POSTs to the server that it should shut down if it was launched as a
Expand All @@ -222,7 +255,7 @@ function closeTool(exitCode=0) {
if (!toolClosed) {
toolClosed = true;
let url = '/api/closetool';
navigator.sendBeacon(url, JSON.stringify({exitCode}));
sendBeacon(url, {exitCode});
window.close();
}
}
Expand Down