avatars: Encode version into the filename.

Hash the salt, user-id, and now avatar version into the filename.
This allows the URL contents to be immutable, and thus to be marked as
immutable and cacheable.  Since avatars are served unauthenticated,
hashing with a server-side salt makes the current and past avatars not
enumerable.

This requires plumbing the current (or future) avatar version through
various parts of the upload process.

Since this already requires a full migration of current avatars, also
take the opportunity to fix the missing `.png` on S3 uploads (#12852).

We switch from SHA-1 to SHA-256, but truncate it such that avatar URL
data does not substantially increase in size.

Fixes: #12852.
This commit is contained in:
Alex Vandiver
2024-06-13 12:57:18 +00:00
committed by Tim Abbott
parent feca9939bb
commit e29a455b2d
21 changed files with 288 additions and 107 deletions

View File

@@ -30,7 +30,7 @@ def _transfer_avatar_to_s3(user: UserProfile) -> None:
file_path = os.path.join(settings.LOCAL_AVATARS_DIR, avatar_path)
try:
with open(file_path + ".original", "rb") as f:
upload_avatar_image(f, user, backend=s3backend)
upload_avatar_image(f, user, backend=s3backend, future=False)
logging.info("Uploaded avatar for %s in realm %s", user.id, user.realm.name)
except FileNotFoundError:
pass
@@ -66,7 +66,7 @@ def _transfer_message_files_to_s3(attachment: Attachment) -> None:
guessed_type,
attachment.owner,
f.read(),
settings.S3_UPLOADS_STORAGE_CLASS,
storage_class=settings.S3_UPLOADS_STORAGE_CLASS,
)
logging.info("Uploaded message file in path %s", file_path)
except FileNotFoundError: # nocoverage