From a435e1569825935bdac2667efdef0d7ab5d879c2 Mon Sep 17 00:00:00 2001 From: Serge Bazanski Date: Sun, 8 Oct 2023 18:13:58 +0200 Subject: [PATCH] Implement LDAP avatar serving This adds a /avatar/ endpoint which serves any jpegPhoto associated with a given user account. In true 'lol ldap' fashion, only `photo` and `jpegPhoto` fields are defined. The first one is for G3 photos (a fax format!). The latter is technically for JPEG. But we expect to abuse this and basically contain _any_ sensible photo format in there, as long as Python's PIL can parse it. The serving function always resamples images to a 256x256 PNG. This makes sure people don't leak EXIF and lets us depend on square avatars. This entire code assumes that it is safe to PIL.Image.open untrusted user data. My understanding is that it is, bar some DoS for very large images. We limit the potential for DoS by storing the images in LDAP, which I hope has some kind of field length limit... Oh, and this also adds a 'default avatar' functionality which serves simple generative mermaid art for any user who doesn't have an explicit avatar set. To prevent leaking the existence of users who don't have an avatar set, we serve such a generated avatar for all UIDs, including UIDs which don't exist. --- poetry.lock | 69 ++++++++++++++- pyproject.toml | 1 + syrenka.png | Bin 0 -> 48772 bytes webapp/__init__.py | 4 +- webapp/avatar.py | 215 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 syrenka.png create mode 100644 webapp/avatar.py diff --git a/poetry.lock b/poetry.lock index e17a523..58d0601 100644 --- a/poetry.lock +++ b/poetry.lock @@ -187,6 +187,73 @@ files = [ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] +[[package]] +name = "pillow" +version = "10.0.1" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "Pillow-10.0.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:8f06be50669087250f319b706decf69ca71fdecd829091a37cc89398ca4dc17a"}, + {file = "Pillow-10.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50bd5f1ebafe9362ad622072a1d2f5850ecfa44303531ff14353a4059113b12d"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6a90167bcca1216606223a05e2cf991bb25b14695c518bc65639463d7db722d"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f11c9102c56ffb9ca87134bd025a43d2aba3f1155f508eff88f694b33a9c6d19"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:186f7e04248103482ea6354af6d5bcedb62941ee08f7f788a1c7707bc720c66f"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0462b1496505a3462d0f35dc1c4d7b54069747d65d00ef48e736acda2c8cbdff"}, + {file = "Pillow-10.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d889b53ae2f030f756e61a7bff13684dcd77e9af8b10c6048fb2c559d6ed6eaf"}, + {file = "Pillow-10.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:552912dbca585b74d75279a7570dd29fa43b6d93594abb494ebb31ac19ace6bd"}, + {file = "Pillow-10.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:787bb0169d2385a798888e1122c980c6eff26bf941a8ea79747d35d8f9210ca0"}, + {file = "Pillow-10.0.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fd2a5403a75b54661182b75ec6132437a181209b901446ee5724b589af8edef1"}, + {file = "Pillow-10.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d7e91b4379f7a76b31c2dda84ab9e20c6220488e50f7822e59dac36b0cd92b1"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19e9adb3f22d4c416e7cd79b01375b17159d6990003633ff1d8377e21b7f1b21"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93139acd8109edcdeffd85e3af8ae7d88b258b3a1e13a038f542b79b6d255c54"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:92a23b0431941a33242b1f0ce6c88a952e09feeea9af4e8be48236a68ffe2205"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cbe68deb8580462ca0d9eb56a81912f59eb4542e1ef8f987405e35a0179f4ea2"}, + {file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:522ff4ac3aaf839242c6f4e5b406634bfea002469656ae8358644fc6c4856a3b"}, + {file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:84efb46e8d881bb06b35d1d541aa87f574b58e87f781cbba8d200daa835b42e1"}, + {file = "Pillow-10.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:898f1d306298ff40dc1b9ca24824f0488f6f039bc0e25cfb549d3195ffa17088"}, + {file = "Pillow-10.0.1-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:bcf1207e2f2385a576832af02702de104be71301c2696d0012b1b93fe34aaa5b"}, + {file = "Pillow-10.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d6c9049c6274c1bb565021367431ad04481ebb54872edecfcd6088d27edd6ed"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28444cb6ad49726127d6b340217f0627abc8732f1194fd5352dec5e6a0105635"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de596695a75496deb3b499c8c4f8e60376e0516e1a774e7bc046f0f48cd620ad"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:2872f2d7846cf39b3dbff64bc1104cc48c76145854256451d33c5faa55c04d1a"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4ce90f8a24e1c15465048959f1e94309dfef93af272633e8f37361b824532e91"}, + {file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ee7810cf7c83fa227ba9125de6084e5e8b08c59038a7b2c9045ef4dde61663b4"}, + {file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1be1c872b9b5fcc229adeadbeb51422a9633abd847c0ff87dc4ef9bb184ae08"}, + {file = "Pillow-10.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:98533fd7fa764e5f85eebe56c8e4094db912ccbe6fbf3a58778d543cadd0db08"}, + {file = "Pillow-10.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:764d2c0daf9c4d40ad12fbc0abd5da3af7f8aa11daf87e4fa1b834000f4b6b0a"}, + {file = "Pillow-10.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fcb59711009b0168d6ee0bd8fb5eb259c4ab1717b2f538bbf36bacf207ef7a68"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:697a06bdcedd473b35e50a7e7506b1d8ceb832dc238a336bd6f4f5aa91a4b500"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f665d1e6474af9f9da5e86c2a3a2d2d6204e04d5af9c06b9d42afa6ebde3f21"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:2fa6dd2661838c66f1a5473f3b49ab610c98a128fc08afbe81b91a1f0bf8c51d"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:3a04359f308ebee571a3127fdb1bd01f88ba6f6fb6d087f8dd2e0d9bff43f2a7"}, + {file = "Pillow-10.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:723bd25051454cea9990203405fa6b74e043ea76d4968166dfd2569b0210886a"}, + {file = "Pillow-10.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:71671503e3015da1b50bd18951e2f9daf5b6ffe36d16f1eb2c45711a301521a7"}, + {file = "Pillow-10.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:44e7e4587392953e5e251190a964675f61e4dae88d1e6edbe9f36d6243547ff3"}, + {file = "Pillow-10.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:3855447d98cced8670aaa63683808df905e956f00348732448b5a6df67ee5849"}, + {file = "Pillow-10.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ed2d9c0704f2dc4fa980b99d565c0c9a543fe5101c25b3d60488b8ba80f0cce1"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5bb289bb835f9fe1a1e9300d011eef4d69661bb9b34d5e196e5e82c4cb09b37"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a0d3e54ab1df9df51b914b2233cf779a5a10dfd1ce339d0421748232cea9876"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:2cc6b86ece42a11f16f55fe8903595eff2b25e0358dec635d0a701ac9586588f"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ca26ba5767888c84bf5a0c1a32f069e8204ce8c21d00a49c90dabeba00ce0145"}, + {file = "Pillow-10.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f0b4b06da13275bc02adfeb82643c4a6385bd08d26f03068c2796f60d125f6f2"}, + {file = "Pillow-10.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bc2e3069569ea9dbe88d6b8ea38f439a6aad8f6e7a6283a38edf61ddefb3a9bf"}, + {file = "Pillow-10.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:8b451d6ead6e3500b6ce5c7916a43d8d8d25ad74b9102a629baccc0808c54971"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:32bec7423cdf25c9038fef614a853c9d25c07590e1a870ed471f47fb80b244db"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cf63d2c6928b51d35dfdbda6f2c1fddbe51a6bc4a9d4ee6ea0e11670dd981e"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f6d3d4c905e26354e8f9d82548475c46d8e0889538cb0657aa9c6f0872a37aa4"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:847e8d1017c741c735d3cd1883fa7b03ded4f825a6e5fcb9378fd813edee995f"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7f771e7219ff04b79e231d099c0a28ed83aa82af91fd5fa9fdb28f5b8d5addaf"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459307cacdd4138edee3875bbe22a2492519e060660eaf378ba3b405d1c66317"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b059ac2c4c7a97daafa7dc850b43b2d3667def858a4f112d1aa082e5c3d6cf7d"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6caf3cd38449ec3cd8a68b375e0c6fe4b6fd04edb6c9766b55ef84a6e8ddf2d"}, + {file = "Pillow-10.0.1.tar.gz", hash = "sha256:d72967b06be9300fed5cfbc8b5bafceec48bf7cdc7dab66b1d2549035287191d"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + [[package]] name = "pyasn1" version = "0.5.0" @@ -273,4 +340,4 @@ email = ["email-validator"] [metadata] lock-version = "2.0" python-versions = "~3.11" -content-hash = "b026e5d2d31ebde4256e7793a62fbba895b049cfc270bf24ab1a0ed352f9d933" +content-hash = "5fe83018dde9d22d0820c713b674849afb60d077ad33429fcfa330cafcba4ef0" diff --git a/pyproject.toml b/pyproject.toml index d4f355e..d971f13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ itsdangerous = "^2.0" Jinja2 = "^3.0" kerberos = "^1.3.0" MarkupSafe = "^2.0" +pillow = "^10.0.1" python-ldap = "^3.2.0" uWSGI = "^2.0" Werkzeug = "^2.0" diff --git a/syrenka.png b/syrenka.png new file mode 100644 index 0000000000000000000000000000000000000000..5001f357c6749be9c6dfd52713607e881cb35ae1 GIT binary patch literal 48772 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 Image: + """ + Resize an image to a square. Can make an image bigger to make it fit or smaller if it doesn't fit. It also crops + part of the image. + + :param self: + :param image: Image to resize. + :param length: Width and height of the output image. + :return: Return the resized image. + """ + + """ + Resizing strategy : + 1) We resize the smallest side to the desired dimension (e.g. 1080) + 2) We crop the other side so as to make it fit with the same length as the smallest side (e.g. 1080) + """ + if image.size[0] < image.size[1]: + # The image is in portrait mode. Height is bigger than width. + + # This makes the width fit the LENGTH in pixels while conserving the ration. + resized_image = image.resize((length, int(image.size[1] * (length / image.size[0])))) + + # Amount of pixel to lose in total on the height of the image. + required_loss = (resized_image.size[1] - length) + + # Crop the height of the image so as to keep the center part. + resized_image = resized_image.crop( + box=(0, required_loss / 2, length, resized_image.size[1] - required_loss / 2)) + + # We now have a length*length pixels image. + return resized_image + else: + # This image is in landscape mode or already squared. The width is bigger than the heihgt. + + # This makes the height fit the LENGTH in pixels while conserving the ration. + resized_image = image.resize((int(image.size[0] * (length / image.size[1])), length)) + + # Amount of pixel to lose in total on the width of the image. + required_loss = resized_image.size[0] - length + + # Crop the width of the image so as to keep 1080 pixels of the center part. + resized_image = resized_image.crop( + box=(required_loss / 2, 0, resized_image.size[0] - required_loss / 2, length)) + + # We now have a length*length pixels image. + return resized_image + + +syrenka = Image.open("syrenka.png") + + +def default_avatar(uid: str) -> Image: + """ + Create little generative avatar for people who don't have a custom one + configured. + """ + img = Image.new('RGBA', (256, 256), (255, 255, 255, 0)) + draw = ImageDraw.Draw(img) + + # Deterministic rng for stable output. + rng = random.Random(uid) + + # Pick a nice random neon color. + n_h, n_s, n_l = rng.random(), 0.5 + rng.random()/2.0, 0.4 + rng.random()/5.0 + + # Use muted version for background. + r, g, b = [int(256*i) for i in colorsys.hls_to_rgb(n_h, n_l+0.3, n_s-0.1)] + draw.rectangle([(0, 0), (256, 256)], fill=(r,g,b,255)) + + # Scale logo by randomized factor. + factor = 0.7 + 0.1 * rng.random() + w, h = int(syrenka.size[0] * factor), int(syrenka.size[1] * factor) + overlay = syrenka.resize((w, h)) + + # Crop to headshot. + overlay = overlay.crop(box=(0, 0, w, w)) + + # Give it a little nudge. + overlay = overlay.rotate((rng.random() - 0.5) * 100) + + # Colorize with full neon color. + r, g, b = [int(256*i) for i in colorsys.hls_to_rgb(n_h,n_l,n_s)] + pixels = overlay.load() + for x in range(img.size[0]): + for y in range(img.size[1]): + alpha = pixels[x, y][3] + pixels[x, y] = (r, g, b, alpha) + + img.alpha_composite(overlay) + + res = io.BytesIO() + img.save(res, 'PNG') + return res.getvalue() + + +class AvatarCacheEntry: + # UID of the avatar's user. + uid: str + # Deadline when this entry expires. + deadline: float + # Cached source bytes + data: bytes + # Cached converted bytes + _converted: bytes + + def __init__(self, uid: str, data: bytes): + self.uid = uid + self.deadline = time.time() + 3600 + self.data = data + self._converted = b"" + + def serve(self): + """ + Serve sanitized image. Always re-encode to PNG 256x256. + """ + # Re-encode to PNG if we haven't yet. + if self._converted == b"": + try: + img = Image.open(io.BytesIO(self.data)) + except Exception as e: + log.warning("Could not parse avatar for {}: {}".format(self.uid, e)) + self.data = default_avatar(self.uid) + img = Image.open(io.BytesIO(self.data)) + + res = io.BytesIO() + img = resize_image(img, 256) + img.save(res, 'PNG') + self._converted = res.getvalue() + + return flask.Response(self._converted, mimetype='image/png') + + +class AvatarCache: + # keyed by uid + entries: dict[str, AvatarCacheEntry] + + def __init__(self): + self.entries = {} + + def get(self, uid: str, bust: bool = False) -> AvatarCacheEntry: + """ + Get avatar, either from cache or from LDAP on cache miss. If 'bust' is + set to True, the cache will not be consulted and the newest result from + LDAP will always be served. + """ + now = time.time() + # Try to get a cache entry to serve, if possible. + if not bust and uid in self.entries: + entry = self.entries[uid] + if entry.deadline > now: + return entry.serve() + + # Otherwise, retrieve from LDAP. + conn = context.get_admin_connection() + try: + dn = ldaputils.user_dn(uid) + res = conn.search_s(dn, ldap.SCOPE_SUBTREE) + except ldap.NO_SUCH_OBJECT: + res = [] + + avatar = None + if len(res) == 1: + for attr, vs in res[0][1].items(): + if attr == 'jpegPhoto': + for v in vs: + try: + avatar = base64.b64decode(v) + except binascii.Error as e: + log.warning("Could not b64decode avatar for {}".format(uid)) + avatar = None + else: + break + break + # If nothing was found in LDAP (either uid doesn't exist or uid doesn't + # have an avatar attached), serve default avatar. + if avatar is None: + avatar = default_avatar(uid) + + # Save avatar in cache. + entry = AvatarCacheEntry(uid, avatar) + self.entries[uid] = entry + + # And serve the entry. + return entry.serve() + +cache = AvatarCache() + +@bp.route('/avatar/', methods=['GET']) +def avatar_serve(uid): + return cache.get(uid)