Skip to content

Commit

Permalink
Change md5 to sha256
Browse files Browse the repository at this point in the history
  • Loading branch information
Wh1isper committed Nov 23, 2023
1 parent 5c68621 commit 0472bd0
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 84 deletions.
10 changes: 5 additions & 5 deletions docs/source/developers/contents.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Models may contain the following entries:
| |``None`` |if any. (:ref:`See |
| | |Below<modelcontent>`) |
+--------------------+-----------+------------------------------+
|**md5** |unicode or |The md5 of the contents. |
|**sha256** |unicode or |The sha256 of the contents. |
| |``None`` | |
| | | |
+--------------------+-----------+------------------------------+
Expand All @@ -80,7 +80,7 @@ model. There are three model types: **notebook**, **file**, and **directory**.
:class:`nbformat.notebooknode.NotebookNode` representing the .ipynb file
represented by the model. See the `NBFormat`_ documentation for a full
description.
- The ``md5`` field a hexdigest string of the md5 value of the notebook
- The ``sha256`` field a hexdigest string of the sha256 value of the notebook
file.

- ``file`` models
Expand All @@ -91,14 +91,14 @@ model. There are three model types: **notebook**, **file**, and **directory**.
file models, ``content`` simply contains the file's bytes after decoding
as UTF-8. Non-text (``base64``) files are read as bytes, base64 encoded,
and then decoded as UTF-8.
- The ``md5`` field a hexdigest string of the md5 value of the file.
- The ``sha256`` field a hexdigest string of the sha256 value of the file.

- ``directory`` models
- The ``format`` field is always ``"json"``.
- The ``mimetype`` field is always ``None``.
- The ``content`` field contains a list of :ref:`content-free<contentfree>`
models representing the entities in the directory.
- The ``md5`` field is always ``None``.
- The ``sha256`` field is always ``None``.

.. note::

Expand Down Expand Up @@ -137,7 +137,7 @@ model. There are three model types: **notebook**, **file**, and **directory**.
"path": "foo/a.ipynb",
"type": "notebook",
"writable": True,
"md5": "7e47382b370c05a1b14706a2a8aff91a",
"sha256": "f5e43a0b1c2e7836ab3b4d6b1c35c19e2558688de15a6a14e137a59e4715d34b",
}
# Notebook Model without Content
Expand Down
12 changes: 6 additions & 6 deletions jupyter_server/services/api/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ paths:
in: query
description: "Return content (0 for no content, 1 for return content)"
type: integer
- name: md5
- name: sha256
in: query
description: "Return md5 hexdigest string of content (0 for no md5, 1 for return md5)"
description: "Return sha256 hexdigest string of content (0 for no sha256, 1 for return sha256)"
type: integer
responses:
404:
Expand Down Expand Up @@ -889,7 +889,7 @@ definitions:
kernel:
$ref: "#/definitions/Kernel"
Contents:
description: "A contents object. The content and format keys may be null if content is not contained. The md5 maybe null if md5 is not contained. If type is 'file', then the mimetype will be null."
description: "A contents object. The content and format keys may be null if content is not contained. The sha256 maybe null if sha256 is not contained. If type is 'file', then the mimetype will be null."
type: object
required:
- type
Expand All @@ -901,7 +901,7 @@ definitions:
- mimetype
- format
- content
- md5
- sha256
properties:
name:
type: string
Expand Down Expand Up @@ -939,9 +939,9 @@ definitions:
format:
type: string
description: Format of content (one of null, 'text', 'base64', 'json')
md5:
sha256:
type: string
description: "The md5 hexdigest string of content, if requested (otherwise null)."
description: "The sha256 hexdigest string of content, if requested (otherwise null)."
Checkpoints:
description: A checkpoint object.
type: object
Expand Down
16 changes: 8 additions & 8 deletions jupyter_server/services/contents/fileio.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,11 +357,11 @@ def _save_file(self, os_path, content, format):
with self.atomic_writing(os_path, text=False) as f:
f.write(bcontent)

def _get_md5(self, os_path):
def _get_sha256(self, os_path):
c, _ = self._read_file(os_path, "byte")
md5 = hashlib.md5()
md5.update(c)
return md5.hexdigest()
sha256 = hashlib.sha256()
sha256.update(c)
return sha256.hexdigest()


class AsyncFileManagerMixin(FileManagerMixin):
Expand Down Expand Up @@ -475,8 +475,8 @@ async def _save_file(self, os_path, content, format):
with self.atomic_writing(os_path, text=False) as f:
await run_sync(f.write, bcontent)

async def _get_md5(self, os_path):
async def _get_sha256(self, os_path):
c, _ = await self._read_file(os_path, "byte")
md5 = hashlib.md5()
await run_sync(md5.update, c)
return md5.hexdigest()
sha256 = hashlib.sha256()
await run_sync(sha256.update, c)
return sha256.hexdigest()
54 changes: 27 additions & 27 deletions jupyter_server/services/contents/filemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class FileContentsManager(FileManagerMixin, ContentsManager):
root_dir = Unicode(config=True)

max_copy_folder_size_mb = Int(500, config=True, help="The max folder size that can be copied")
support_md5 = Bool(True, config=False, help="Support md5 argument in `get`")
support_sha256 = Bool(True, config=False, help="Support sha256 argument in `get`")

@default("root_dir")
def _default_root_dir(self):
Expand Down Expand Up @@ -269,7 +269,7 @@ def _base_model(self, path):
model["mimetype"] = None
model["size"] = size
model["writable"] = self.is_writable(path)
model["md5"] = None
model["sha256"] = None

return model

Expand Down Expand Up @@ -337,7 +337,7 @@ def _dir_model(self, path, content=True):

return model

def _file_model(self, path, content=True, format=None, md5=False):
def _file_model(self, path, content=True, format=None, sha256=False):
"""Build a model for a file
if content is requested, include the file contents.
Expand Down Expand Up @@ -366,13 +366,13 @@ def _file_model(self, path, content=True, format=None, md5=False):
content=content,
format=format,
)
if md5:
md5 = self._get_md5(os_path)
model.update(md5=md5)
if sha256:
sha256 = self._get_sha256(os_path)
model.update(sha256=sha256)

return model

def _notebook_model(self, path, content=True, md5=False):
def _notebook_model(self, path, content=True, sha256=False):
"""Build a notebook model
if content is requested, the notebook content will be populated
Expand All @@ -391,12 +391,12 @@ def _notebook_model(self, path, content=True, md5=False):
model["content"] = nb
model["format"] = "json"
self.validate_notebook_model(model, validation_error)
if md5:
model["md5"] = self._get_md5(os_path)
if sha256:
model["sha256"] = self._get_sha256(os_path)

return model

def get(self, path, content=True, type=None, format=None, md5=None):
def get(self, path, content=True, type=None, format=None, sha256=None):
"""Takes a path for an entity and returns its model
Parameters
Expand All @@ -411,8 +411,8 @@ def get(self, path, content=True, type=None, format=None, md5=None):
format : str, optional
The requested format for file contents. 'text' or 'base64'.
Ignored if this returns a notebook or directory model.
md5: bool, optional
Whether to include the md5 of the file contents.
sha256: bool, optional
Whether to include the sha256 of the file contents.
Returns
-------
Expand Down Expand Up @@ -440,11 +440,11 @@ def get(self, path, content=True, type=None, format=None, md5=None):
)
model = self._dir_model(path, content=content)
elif type == "notebook" or (type is None and path.endswith(".ipynb")):
model = self._notebook_model(path, content=content, md5=md5)
model = self._notebook_model(path, content=content, sha256=sha256)
else:
if type == "directory":
raise web.HTTPError(400, "%s is not a directory" % path, reason="bad type")
model = self._file_model(path, content=content, format=format, md5=md5)
model = self._file_model(path, content=content, format=format, sha256=sha256)
self.emit(data={"action": "get", "path": path})
return model

Expand Down Expand Up @@ -726,7 +726,7 @@ def _human_readable_size(self, size):
class AsyncFileContentsManager(FileContentsManager, AsyncFileManagerMixin, AsyncContentsManager):
"""An async file contents manager."""

support_md5 = Bool(True, config=False, help="Support md5 argument in `get`")
support_sha256 = Bool(True, config=False, help="Support sha256 argument in `get`")

@default("checkpoints_class")
def _checkpoints_class_default(self):
Expand Down Expand Up @@ -797,7 +797,7 @@ async def _dir_model(self, path, content=True):

return model

async def _file_model(self, path, content=True, format=None, md5=False):
async def _file_model(self, path, content=True, format=None, sha256=False):
"""Build a model for a file
if content is requested, include the file contents.
Expand Down Expand Up @@ -826,13 +826,13 @@ async def _file_model(self, path, content=True, format=None, md5=False):
content=content,
format=format,
)
if md5:
md5 = await self._get_md5(os_path)
model.update(md5=md5)
if sha256:
sha256 = await self._get_sha256(os_path)
model.update(sha256=sha256)

return model

async def _notebook_model(self, path, content=True, md5=False):
async def _notebook_model(self, path, content=True, sha256=False):
"""Build a notebook model
if content is requested, the notebook content will be populated
Expand All @@ -851,12 +851,12 @@ async def _notebook_model(self, path, content=True, md5=False):
model["content"] = nb
model["format"] = "json"
self.validate_notebook_model(model, validation_error)
if md5:
model["md5"] = await self._get_md5(os_path)
if sha256:
model["sha256"] = await self._get_sha256(os_path)

return model

async def get(self, path, content=True, type=None, format=None, md5=False):
async def get(self, path, content=True, type=None, format=None, sha256=False):
"""Takes a path for an entity and returns its model
Parameters
Expand All @@ -871,8 +871,8 @@ async def get(self, path, content=True, type=None, format=None, md5=False):
format : str, optional
The requested format for file contents. 'text' or 'base64'.
Ignored if this returns a notebook or directory model.
md5: bool, optional
Whether to include the md5 of the file contents.
sha256: bool, optional
Whether to include the sha256 of the file contents.
Returns
-------
Expand All @@ -895,11 +895,11 @@ async def get(self, path, content=True, type=None, format=None, md5=False):
)
model = await self._dir_model(path, content=content)
elif type == "notebook" or (type is None and path.endswith(".ipynb")):
model = await self._notebook_model(path, content=content, md5=md5)
model = await self._notebook_model(path, content=content, sha256=sha256)
else:
if type == "directory":
raise web.HTTPError(400, "%s is not a directory" % path, reason="bad type")
model = await self._file_model(path, content=content, format=format, md5=md5)
model = await self._file_model(path, content=content, format=format, sha256=sha256)
self.emit(data={"action": "get", "path": path})
return model

Expand Down
34 changes: 17 additions & 17 deletions jupyter_server/services/contents/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ def _validate_in_or_not(expect_in_model: bool, model: Dict[str, Any], maybe_none
)


def validate_model(model, expect_content, expect_md5):
def validate_model(model, expect_content, expect_sha256):
"""
Validate a model returned by a ContentsManager method.
If expect_content is True, then we expect non-null entries for 'content'
and 'format'.
If expect_md5 is True, then we expect non-null entries for 'md5'.
If expect_sha256 is True, then we expect non-null entries for 'sha256'.
"""
required_keys = {
"name",
Expand All @@ -63,7 +63,7 @@ def validate_model(model, expect_content, expect_md5):
"mimetype",
"content",
"format",
"md5",
"sha256",
}
missing = required_keys - set(model.keys())
if missing:
Expand All @@ -73,9 +73,9 @@ def validate_model(model, expect_content, expect_md5):
)

content_keys = ["content", "format"]
md5_keys = ["md5"]
sha256_keys = ["sha256"]
_validate_in_or_not(expect_content, model, content_keys)
_validate_in_or_not(expect_md5, model, md5_keys)
_validate_in_or_not(expect_sha256, model, sha256_keys)


class ContentsAPIHandler(APIHandler):
Expand Down Expand Up @@ -136,10 +136,10 @@ async def get(self, path=""):
raise web.HTTPError(400, "Content %r is invalid" % content_str)
content = int(content_str or "")

md5_str = self.get_query_argument("md5", default="0")
if md5_str not in {"0", "1"}:
raise web.HTTPError(400, "Content %r is invalid" % md5_str)
md5 = int(md5_str or "")
sha256_str = self.get_query_argument("sha256", default="0")
if sha256_str not in {"0", "1"}:
raise web.HTTPError(400, "Content %r is invalid" % sha256_str)
sha256 = int(sha256_str or "")

if not cm.allow_hidden and await ensure_async(cm.is_hidden(path)):
await self._finish_error(
Expand All @@ -151,12 +151,12 @@ async def get(self, path=""):
"format": format,
"content": content,
}
if cm.support_md5:
kwargs["md5"] = md5
if cm.support_sha256:
kwargs["sha256"] = sha256

try:
model = await ensure_async(self.contents_manager.get(**kwargs))
validate_model(model, expect_content=content, expect_md5=md5)
validate_model(model, expect_content=content, expect_sha256=sha256)
self._finish_model(model, location=False)
except web.HTTPError as exc:
# 404 is okay in this context, catch exception and return 404 code to prevent stack trace on client
Expand Down Expand Up @@ -186,7 +186,7 @@ async def patch(self, path=""):
raise web.HTTPError(400, f"Cannot rename file or directory {path!r}")

model = await ensure_async(cm.update(model, path))
validate_model(model, expect_content=False, expect_md5=False)
validate_model(model, expect_content=False, expect_sha256=False)
self._finish_model(model)

async def _copy(self, copy_from, copy_to=None):
Expand All @@ -199,15 +199,15 @@ async def _copy(self, copy_from, copy_to=None):
)
model = await ensure_async(self.contents_manager.copy(copy_from, copy_to))
self.set_status(201)
validate_model(model, expect_content=False, expect_md5=False)
validate_model(model, expect_content=False, expect_sha256=False)
self._finish_model(model)

async def _upload(self, model, path):
"""Handle upload of a new file to path"""
self.log.info("Uploading file to %s", path)
model = await ensure_async(self.contents_manager.new(model, path))
self.set_status(201)
validate_model(model, expect_content=False, expect_md5=False)
validate_model(model, expect_content=False, expect_sha256=False)
self._finish_model(model)

async def _new_untitled(self, path, type="", ext=""):
Expand All @@ -217,7 +217,7 @@ async def _new_untitled(self, path, type="", ext=""):
self.contents_manager.new_untitled(path=path, type=type, ext=ext)
)
self.set_status(201)
validate_model(model, expect_content=False, expect_md5=False)
validate_model(model, expect_content=False, expect_sha256=False)
self._finish_model(model)

async def _save(self, model, path):
Expand All @@ -226,7 +226,7 @@ async def _save(self, model, path):
if not chunk or chunk == -1: # Avoid tedious log information
self.log.info("Saving file at %s", path)
model = await ensure_async(self.contents_manager.save(model, path))
validate_model(model, expect_content=False, expect_md5=False)
validate_model(model, expect_content=False, expect_sha256=False)
self._finish_model(model)

@web.authenticated
Expand Down
Loading

0 comments on commit 0472bd0

Please sign in to comment.