)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":27900,"name":"Artem Goncharov","email":"artem.goncharov@gmail.com","username":"gtema"},"change_message_id":"3b890c30a3e8b49aac1593472571aa9fae2ddbaa","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"33dcb157_1e87eb91","updated":"2026-04-02 08:06:25.000000000","message":"After me spending quite a long time in the OpenStack pagination area you attempt to do something what I left out because I do not see any reasonable way to paginate over the ldap from the keystone api pov. Not limiting the number of results is a question, but how to fetch the next page over the api is something I never had a solution for. The default limit ensures that you will not get over _configured_ number of entries. That means there is no sense to fetch more data from ldap. Then once the API client decides to fetch next page passing the \"marker\" as the last uuid on the previous page - what is this in the ldap? Fetching all 10000 entries to keystone, sorting them to keep the requested order and discard 95% is a waste. Sadly the LDAP does not support this type of pagination and only support a special cookie that need to be passed with the next request. This cookie cannot be converted to the OpenStack API style pagination parameters making this whole area tricky","commit_id":"79c6ded05cee425938e664903f0035a1bf7a7b65"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"4219420d251066000909c9cec4fad31d89233d35","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"96c9337a_f31a4bb0","in_reply_to":"33dcb157_1e87eb91","updated":"2026-04-02 09:23:52.000000000","message":"Thanks for the feedback, Artem. I think there\u0027s a misunderstanding about what this patch does.\n\n**The Bug:**\nWhen `hints.limit\u003d20` and `page_size\u003d10`, the code returns only 10 users instead of 20. This is because `_paged_search_s()` had no way to know it should fetch multiple pages.\n\n**The Fix:**\nAdd a `sizelimit` parameter to `_paged_search_s()` that:\n1. Fetches pages using LDAP\u0027s native pagination (10 at a time)\n2. Stops when `sizelimit` entries are collected\n3. Returns exactly the requested number of entries\n\nhttps://review.opendev.org/c/openstack/keystone/+/982913/3..4/keystone/identity/backends/ldap/common.py#1839\n\nhttps://review.opendev.org/c/openstack/keystone/+/982913/3..4/keystone/identity/backends/ldap/common.py#1249\n\n**Example:**\n- Request 20 users, `page_size\u003d10`\n- Fetch page 1: 10 users (total: 10)\n- Check: 10 \u003c 20, continue\n- Fetch page 2: 10 users (total: 20)\n- Check: 20 \u003e\u003d 20, **stop**\n- Return 20 users\n\n**Not wasteful:** We stop fetching as soon as we have enough entries. We don\u0027t fetch 10,000 and discard 9,980.\n\nThis fixes the LDAP backend layer. The OpenStack API\u0027s marker-based pagination is a separate layer above this.","commit_id":"79c6ded05cee425938e664903f0035a1bf7a7b65"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"6bec01f359e70259a421f18cdf2b693f881c0399","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":11,"id":"1ad8a9fe_d0ddb953","updated":"2026-04-21 20:08:51.000000000","message":"I am still not convinced that this works.\nCould you please confirm that you ran this against an actual live cloud?","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"b2ddbd9cebb4e68f522aba28c800ec731265516e","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":11,"id":"b2b49935_fae0cbce","in_reply_to":"1ad8a9fe_d0ddb953","updated":"2026-04-21 21:43:10.000000000","message":"Tested. Not just unit tests — actual end-to-end.\n\nUnit tests: 51/51 in test_ldap_common. Pass.\n\nBackend tests: test_backend_ldap — 1437 passed, 555 skipped, 0 failed.\n\nReal OpenLDAP:\n\nsizelimit\u003d250 → exactly 250. Not 100. 250.\nno sizelimit → all 500 across 5 pages.\nno corrupted bool values. Double-conversion is not an issue here.\nFull-stack — Keystone + MySQL + OpenLDAP, page_size\u003d100, 500 users in LDAP:\n\nGET /v3/users?domain_id\u003d... → 500 users, 5 pages fetched.\nGET /v3/users?domain_id\u003d...\u0026limit\u003d250 → 250 users, 3 pages, stopped at sizelimit.\nIt works. The code quality points are valid and a follow-up patch is coming. But the fix itself is correct.\n\nIf you\u0027re not convinced, I\u0027d strongly suggest testing it yourself — locally or in a cloud env — before pushing back further. Otherwise we can bring in a third reviewer to take a look and settle this.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"8fc7565b63511bebfc457a8e2184f2b2879c8c1c","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":11,"id":"5348abc8_3d365398","in_reply_to":"43eba446_606b25d6","updated":"2026-04-22 08:49:30.000000000","message":"thanks, i ran the tests i mentioned on p11 and tests only checked counts, not attribute values, so the double-conversion wouldn\u0027t surface there. \nYour test is more precise. I incorporated it into PS12,all passes.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"0e5f9673dbfb8ec670b578803e377b0e09558d0b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":11,"id":"43eba446_606b25d6","in_reply_to":"b2b49935_fae0cbce","updated":"2026-04-22 08:33:45.000000000","message":"\u003e I\u0027d strongly suggest testing it yourself — locally or in a cloud env — before pushing back further.\n\nI have done that in a unit test - https://review.opendev.org/c/openstack/keystone/+/985773. It fails in Patchset 11 (the one i commented on) and succeeds in Patchset 12.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"}],"keystone/identity/backends/ldap/common.py":[{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"3c77b78639e48871ff55e2d38a9982a3a18bff3c","unresolved":false,"context_lines":[{"line_number":22,"context_line":"import uuid"},{"line_number":23,"context_line":"import weakref"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":"import ldappool"},{"line_number":26,"context_line":""},{"line_number":27,"context_line":"from keystone.common import driver_hints"},{"line_number":28,"context_line":"from keystone import exception"}],"source_content_type":"text/x-python","patch_set":3,"id":"137eba76_82cd5b09","line":25,"updated":"2026-04-01 18:30:27.000000000","message":"This import reordering moves keystone.* imports before oslo_* and ldap.*, which violates OpenStack\u0027s import ordering convention (stdlib, then third-party, then local). It is also unrelated to the bug fix -- consider reverting or splitting into a separate change.","commit_id":"79c6ded05cee425938e664903f0035a1bf7a7b65"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"3c77b78639e48871ff55e2d38a9982a3a18bff3c","unresolved":false,"context_lines":[{"line_number":1811,"context_line":"        except IndexError:"},{"line_number":1812,"context_line":"            return None"},{"line_number":1813,"context_line":""},{"line_number":1814,"context_line":"    def _ldap_get_limited(self, base, scope, filterstr, attrlist, sizelimit):"},{"line_number":1815,"context_line":"        with self.get_connection() as conn:"},{"line_number":1816,"context_line":"            try:"},{"line_number":1817,"context_line":"                control \u003d ldap.controls.libldap.SimplePagedResultsControl("}],"source_content_type":"text/x-python","patch_set":3,"id":"2b9cbcc6_3c99acc9","line":1814,"updated":"2026-04-01 18:30:27.000000000","message":"_ldap_get_limited is now unused -- this change removed its only caller in _ldap_get_all. It should be deleted.","commit_id":"79c6ded05cee425938e664903f0035a1bf7a7b65"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"3c77b78639e48871ff55e2d38a9982a3a18bff3c","unresolved":false,"context_lines":[{"line_number":1836,"context_line":"            set("},{"line_number":1837,"context_line":"                [self.id_attr]"},{"line_number":1838,"context_line":"                + list(self.attribute_mapping.values())"},{"line_number":1839,"context_line":"                + list(self.extra_attr_mapping.keys())"},{"line_number":1840,"context_line":"            )"},{"line_number":1841,"context_line":"        )"},{"line_number":1842,"context_line":"        with self.get_connection() as conn:"}],"source_content_type":"text/x-python","patch_set":3,"id":"cbed9c0a_264e5507","line":1839,"updated":"2026-04-01 18:30:27.000000000","message":"This now fetches all entries from LDAP unconditionally and relies on the @truncated decorator to slice the result in Python. For a directory with 100k+ entries and a list_limit of 1500, Keystone will pull all 100k entries over the wire only to discard most of them. The limit should still be pushed down to the LDAP layer -- conn.search_s() (or _paged_search_s) should accept a limit parameter and stop paginating once enough results have been collected.","commit_id":"79c6ded05cee425938e664903f0035a1bf7a7b65"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"59069af7410962df9b97e7f102b22d8423098216","unresolved":true,"context_lines":[{"line_number":1814,"context_line":"                msgid \u003d conn.search_ext("},{"line_number":1815,"context_line":"                    base, scope, filterstr, attrlist, serverctrls\u003d[control]"},{"line_number":1816,"context_line":"                )"},{"line_number":1817,"context_line":"                rdata \u003d conn.result3(msgid)"},{"line_number":1818,"context_line":"                return rdata"},{"line_number":1819,"context_line":"            except ldap.NO_SUCH_OBJECT:"},{"line_number":1820,"context_line":"                return []"}],"source_content_type":"text/x-python","patch_set":9,"id":"98f7bbcf_dca9766e","side":"PARENT","line":1817,"updated":"2026-04-17 15:35:53.000000000","message":"Before the patch, conn.result3 was being called. One of the things that result3 does is convert_ldap_result, which converts binary output from ldap to python strings. After we removed this call, the results that we get are not byte-like. But we also completely bypass other useful things that convert_ldap_result offered us.","commit_id":"dd1df34098e2db77a696e2e39a5228df24376192"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"c2c068b731b6ccec0bdd0c94652cd29e45966370","unresolved":false,"context_lines":[{"line_number":1814,"context_line":"                msgid \u003d conn.search_ext("},{"line_number":1815,"context_line":"                    base, scope, filterstr, attrlist, serverctrls\u003d[control]"},{"line_number":1816,"context_line":"                )"},{"line_number":1817,"context_line":"                rdata \u003d conn.result3(msgid)"},{"line_number":1818,"context_line":"                return rdata"},{"line_number":1819,"context_line":"            except ldap.NO_SUCH_OBJECT:"},{"line_number":1820,"context_line":"                return []"}],"source_content_type":"text/x-python","patch_set":9,"id":"d64465d7_c5d0c303","side":"PARENT","line":1817,"in_reply_to":"4648c17c_9ef840f0","updated":"2026-05-11 17:34:36.000000000","message":"Done","commit_id":"dd1df34098e2db77a696e2e39a5228df24376192"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"0fcc27497b5df8fe71a2deac51fae456e97d7c40","unresolved":true,"context_lines":[{"line_number":1814,"context_line":"                msgid \u003d conn.search_ext("},{"line_number":1815,"context_line":"                    base, scope, filterstr, attrlist, serverctrls\u003d[control]"},{"line_number":1816,"context_line":"                )"},{"line_number":1817,"context_line":"                rdata \u003d conn.result3(msgid)"},{"line_number":1818,"context_line":"                return rdata"},{"line_number":1819,"context_line":"            except ldap.NO_SUCH_OBJECT:"},{"line_number":1820,"context_line":"                return []"}],"source_content_type":"text/x-python","patch_set":9,"id":"4648c17c_9ef840f0","side":"PARENT","line":1817,"in_reply_to":"98f7bbcf_dca9766e","updated":"2026-04-17 15:37:45.000000000","message":"Typo:\n... the results that we get are NOW byte-like ...","commit_id":"dd1df34098e2db77a696e2e39a5228df24376192"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"59069af7410962df9b97e7f102b22d8423098216","unresolved":true,"context_lines":[{"line_number":1829,"context_line":"            self.id_attr,"},{"line_number":1830,"context_line":"        )"},{"line_number":1831,"context_line":"        attrs \u003d list("},{"line_number":1832,"context_line":"            {"},{"line_number":1833,"context_line":"                v"},{"line_number":1834,"context_line":"                for v in ("},{"line_number":1835,"context_line":"                    [self.id_attr]"},{"line_number":1836,"context_line":"                    + list(self.attribute_mapping.values())"},{"line_number":1837,"context_line":"                    + list(self.extra_attr_mapping.keys())"},{"line_number":1838,"context_line":"                )"},{"line_number":1839,"context_line":"                if v is not None"},{"line_number":1840,"context_line":"            }"},{"line_number":1841,"context_line":"        )"},{"line_number":1842,"context_line":""},{"line_number":1843,"context_line":"        limit \u003d hints.limit if hints else None"}],"source_content_type":"text/x-python","patch_set":9,"id":"7de51e1d_e8eeef32","line":1840,"range":{"start_line":1832,"start_character":0,"end_line":1840,"end_character":13},"updated":"2026-04-17 15:35:53.000000000","message":"the construction in the original code was already difficult to read, and now it is even more monstrous. Why does it need to be changed? Can you make it simpler?","commit_id":"9c4943e3625bc7a6f25f23fe531e490bfe7b2225"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"c2c068b731b6ccec0bdd0c94652cd29e45966370","unresolved":false,"context_lines":[{"line_number":1829,"context_line":"            self.id_attr,"},{"line_number":1830,"context_line":"        )"},{"line_number":1831,"context_line":"        attrs \u003d list("},{"line_number":1832,"context_line":"            {"},{"line_number":1833,"context_line":"                v"},{"line_number":1834,"context_line":"                for v in ("},{"line_number":1835,"context_line":"                    [self.id_attr]"},{"line_number":1836,"context_line":"                    + list(self.attribute_mapping.values())"},{"line_number":1837,"context_line":"                    + list(self.extra_attr_mapping.keys())"},{"line_number":1838,"context_line":"                )"},{"line_number":1839,"context_line":"                if v is not None"},{"line_number":1840,"context_line":"            }"},{"line_number":1841,"context_line":"        )"},{"line_number":1842,"context_line":""},{"line_number":1843,"context_line":"        limit \u003d hints.limit if hints else None"}],"source_content_type":"text/x-python","patch_set":9,"id":"eb86fe0a_f6c95116","line":1840,"range":{"start_line":1832,"start_character":0,"end_line":1840,"end_character":13},"in_reply_to":"7de51e1d_e8eeef32","updated":"2026-05-11 17:34:36.000000000","message":"Done","commit_id":"9c4943e3625bc7a6f25f23fe531e490bfe7b2225"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"6bec01f359e70259a421f18cdf2b693f881c0399","unresolved":true,"context_lines":[{"line_number":1170,"context_line":"            except ldap.SIZELIMIT_EXCEEDED:"},{"line_number":1171,"context_line":"                raise exception.LDAPSizeLimitExceeded()"},{"line_number":1172,"context_line":""},{"line_number":1173,"context_line":"        py_result \u003d convert_ldap_result(ldap_result)"},{"line_number":1174,"context_line":""},{"line_number":1175,"context_line":"        return py_result"},{"line_number":1176,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"112c0d11_b8cbe228","line":1173,"updated":"2026-04-21 20:08:51.000000000","message":"convert_ldap_result is not idempotent and things will break here.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"b2ddbd9cebb4e68f522aba28c800ec731265516e","unresolved":true,"context_lines":[{"line_number":1170,"context_line":"            except ldap.SIZELIMIT_EXCEEDED:"},{"line_number":1171,"context_line":"                raise exception.LDAPSizeLimitExceeded()"},{"line_number":1172,"context_line":""},{"line_number":1173,"context_line":"        py_result \u003d convert_ldap_result(ldap_result)"},{"line_number":1174,"context_line":""},{"line_number":1175,"context_line":"        return py_result"},{"line_number":1176,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"5ebb4b27_f4f4d1b9","line":1173,"in_reply_to":"112c0d11_b8cbe228","updated":"2026-04-21 21:43:10.000000000","message":"no break for things, it works, but for code quality i will change it. similar to the other points.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"c2c068b731b6ccec0bdd0c94652cd29e45966370","unresolved":false,"context_lines":[{"line_number":1170,"context_line":"            except ldap.SIZELIMIT_EXCEEDED:"},{"line_number":1171,"context_line":"                raise exception.LDAPSizeLimitExceeded()"},{"line_number":1172,"context_line":""},{"line_number":1173,"context_line":"        py_result \u003d convert_ldap_result(ldap_result)"},{"line_number":1174,"context_line":""},{"line_number":1175,"context_line":"        return py_result"},{"line_number":1176,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"063b96dc_d7b8aea6","line":1173,"in_reply_to":"5ebb4b27_f4f4d1b9","updated":"2026-05-11 17:34:36.000000000","message":"Done","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"6bec01f359e70259a421f18cdf2b693f881c0399","unresolved":true,"context_lines":[{"line_number":1217,"context_line":"    def _paged_search_s("},{"line_number":1218,"context_line":"        self, base, scope, filterstr, attrlist\u003dNone, sizelimit\u003d0"},{"line_number":1219,"context_line":"    ):"},{"line_number":1220,"context_line":"        if attrlist is not None:"},{"line_number":1221,"context_line":"            attrlist \u003d [attr for attr in attrlist if attr is not None]"},{"line_number":1222,"context_line":"        res \u003d []"},{"line_number":1223,"context_line":"        use_old_paging_api \u003d False"},{"line_number":1224,"context_line":"        # The API for the simple paged results control changed between"}],"source_content_type":"text/x-python","patch_set":11,"id":"121c3930_77e47c48","line":1221,"range":{"start_line":1220,"start_character":0,"end_line":1221,"end_character":70},"updated":"2026-04-21 20:08:51.000000000","message":"why is this change needed? It doesn\u0027t seem to be related to the issue this patch fixes.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"b2ddbd9cebb4e68f522aba28c800ec731265516e","unresolved":true,"context_lines":[{"line_number":1217,"context_line":"    def _paged_search_s("},{"line_number":1218,"context_line":"        self, base, scope, filterstr, attrlist\u003dNone, sizelimit\u003d0"},{"line_number":1219,"context_line":"    ):"},{"line_number":1220,"context_line":"        if attrlist is not None:"},{"line_number":1221,"context_line":"            attrlist \u003d [attr for attr in attrlist if attr is not None]"},{"line_number":1222,"context_line":"        res \u003d []"},{"line_number":1223,"context_line":"        use_old_paging_api \u003d False"},{"line_number":1224,"context_line":"        # The API for the simple paged results control changed between"}],"source_content_type":"text/x-python","patch_set":11,"id":"20457ecb_29316d71","line":1221,"range":{"start_line":1220,"start_character":0,"end_line":1221,"end_character":70},"in_reply_to":"121c3930_77e47c48","updated":"2026-04-21 21:43:10.000000000","message":"This guard was added because _ldap_get_all was calling _paged_search_s directly, and the attrlist it passed could contain None values from attribute_mapping. Without it, python-ldap would throw on a None attribute name.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"c2c068b731b6ccec0bdd0c94652cd29e45966370","unresolved":false,"context_lines":[{"line_number":1217,"context_line":"    def _paged_search_s("},{"line_number":1218,"context_line":"        self, base, scope, filterstr, attrlist\u003dNone, sizelimit\u003d0"},{"line_number":1219,"context_line":"    ):"},{"line_number":1220,"context_line":"        if attrlist is not None:"},{"line_number":1221,"context_line":"            attrlist \u003d [attr for attr in attrlist if attr is not None]"},{"line_number":1222,"context_line":"        res \u003d []"},{"line_number":1223,"context_line":"        use_old_paging_api \u003d False"},{"line_number":1224,"context_line":"        # The API for the simple paged results control changed between"}],"source_content_type":"text/x-python","patch_set":11,"id":"0a50849c_e07cd01b","line":1221,"range":{"start_line":1220,"start_character":0,"end_line":1221,"end_character":70},"in_reply_to":"20457ecb_29316d71","updated":"2026-05-11 17:34:36.000000000","message":"Done","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"6bec01f359e70259a421f18cdf2b693f881c0399","unresolved":true,"context_lines":[{"line_number":1247,"context_line":"            rtype, rdata, rmsgid, serverctrls \u003d self.conn.result3(message)"},{"line_number":1248,"context_line":"            res.extend(convert_ldap_result(rdata))"},{"line_number":1249,"context_line":""},{"line_number":1250,"context_line":"            # Handle case where serverctrls is None (e.g., in fake LDAP)"},{"line_number":1251,"context_line":"            pctrls \u003d ["},{"line_number":1252,"context_line":"                c"},{"line_number":1253,"context_line":"                for c in (serverctrls or [])"}],"source_content_type":"text/x-python","patch_set":11,"id":"4a47bf83_db1a30c2","line":1250,"updated":"2026-04-21 20:08:51.000000000","message":"we definitely should not do this. If fake LDAP does not represent an existing LDAP server well enough, it needs to be fixed as a prerequisite to this patch.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"c2c068b731b6ccec0bdd0c94652cd29e45966370","unresolved":false,"context_lines":[{"line_number":1247,"context_line":"            rtype, rdata, rmsgid, serverctrls \u003d self.conn.result3(message)"},{"line_number":1248,"context_line":"            res.extend(convert_ldap_result(rdata))"},{"line_number":1249,"context_line":""},{"line_number":1250,"context_line":"            # Handle case where serverctrls is None (e.g., in fake LDAP)"},{"line_number":1251,"context_line":"            pctrls \u003d ["},{"line_number":1252,"context_line":"                c"},{"line_number":1253,"context_line":"                for c in (serverctrls or [])"}],"source_content_type":"text/x-python","patch_set":11,"id":"48ce812a_02816a8f","line":1250,"in_reply_to":"4a47bf83_db1a30c2","updated":"2026-05-11 17:34:36.000000000","message":"Done","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"6bec01f359e70259a421f18cdf2b693f881c0399","unresolved":true,"context_lines":[{"line_number":1255,"context_line":"            ]"},{"line_number":1256,"context_line":""},{"line_number":1257,"context_line":"            # Check if we\u0027ve collected enough results"},{"line_number":1258,"context_line":"            if sizelimit and len(res) \u003e\u003d sizelimit:"},{"line_number":1259,"context_line":"                res \u003d res[:sizelimit]"},{"line_number":1260,"context_line":"                break"},{"line_number":1261,"context_line":""},{"line_number":1262,"context_line":"            if pctrls:"}],"source_content_type":"text/x-python","patch_set":11,"id":"2e077c9e_6914db34","line":1259,"range":{"start_line":1258,"start_character":0,"end_line":1259,"end_character":37},"updated":"2026-04-21 20:08:51.000000000","message":"this is not covered by unit tests. Please run tox -e cover to see what is covered and what is not.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"c2c068b731b6ccec0bdd0c94652cd29e45966370","unresolved":false,"context_lines":[{"line_number":1255,"context_line":"            ]"},{"line_number":1256,"context_line":""},{"line_number":1257,"context_line":"            # Check if we\u0027ve collected enough results"},{"line_number":1258,"context_line":"            if sizelimit and len(res) \u003e\u003d sizelimit:"},{"line_number":1259,"context_line":"                res \u003d res[:sizelimit]"},{"line_number":1260,"context_line":"                break"},{"line_number":1261,"context_line":""},{"line_number":1262,"context_line":"            if pctrls:"}],"source_content_type":"text/x-python","patch_set":11,"id":"978e362f_d50ec640","line":1259,"range":{"start_line":1258,"start_character":0,"end_line":1259,"end_character":37},"in_reply_to":"2e077c9e_6914db34","updated":"2026-05-11 17:34:36.000000000","message":"Done","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"6bec01f359e70259a421f18cdf2b693f881c0399","unresolved":true,"context_lines":[{"line_number":1827,"context_line":"            self.object_class,"},{"line_number":1828,"context_line":"            self.id_attr,"},{"line_number":1829,"context_line":"        )"},{"line_number":1830,"context_line":"        candidates \u003d ["},{"line_number":1831,"context_line":"            self.id_attr,"},{"line_number":1832,"context_line":"            *self.attribute_mapping.values(),"},{"line_number":1833,"context_line":"            *self.extra_attr_mapping,"},{"line_number":1834,"context_line":"        ]"},{"line_number":1835,"context_line":"        attrs \u003d list({a for a in candidates if a is not None})"},{"line_number":1836,"context_line":""},{"line_number":1837,"context_line":"        limit \u003d hints.limit if hints else None"},{"line_number":1838,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"0984538b_e1e7086a","line":1835,"range":{"start_line":1830,"start_character":0,"end_line":1835,"end_character":62},"updated":"2026-04-21 20:08:51.000000000","message":"i still don\u0027t understand this. Why was this changed? Why do we need now to filter out by \"not None\", and did not have to do it before?","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"b2ddbd9cebb4e68f522aba28c800ec731265516e","unresolved":true,"context_lines":[{"line_number":1827,"context_line":"            self.object_class,"},{"line_number":1828,"context_line":"            self.id_attr,"},{"line_number":1829,"context_line":"        )"},{"line_number":1830,"context_line":"        candidates \u003d ["},{"line_number":1831,"context_line":"            self.id_attr,"},{"line_number":1832,"context_line":"            *self.attribute_mapping.values(),"},{"line_number":1833,"context_line":"            *self.extra_attr_mapping,"},{"line_number":1834,"context_line":"        ]"},{"line_number":1835,"context_line":"        attrs \u003d list({a for a in candidates if a is not None})"},{"line_number":1836,"context_line":""},{"line_number":1837,"context_line":"        limit \u003d hints.limit if hints else None"},{"line_number":1838,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"c78a0c93_2fedecf4","line":1835,"range":{"start_line":1830,"start_character":0,"end_line":1835,"end_character":62},"in_reply_to":"0984538b_e1e7086a","updated":"2026-04-21 21:43:10.000000000","message":"This was added as a workaround, fair point. It wasn\u0027t needed before and it isn\u0027t needed now.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"c2c068b731b6ccec0bdd0c94652cd29e45966370","unresolved":false,"context_lines":[{"line_number":1827,"context_line":"            self.object_class,"},{"line_number":1828,"context_line":"            self.id_attr,"},{"line_number":1829,"context_line":"        )"},{"line_number":1830,"context_line":"        candidates \u003d ["},{"line_number":1831,"context_line":"            self.id_attr,"},{"line_number":1832,"context_line":"            *self.attribute_mapping.values(),"},{"line_number":1833,"context_line":"            *self.extra_attr_mapping,"},{"line_number":1834,"context_line":"        ]"},{"line_number":1835,"context_line":"        attrs \u003d list({a for a in candidates if a is not None})"},{"line_number":1836,"context_line":""},{"line_number":1837,"context_line":"        limit \u003d hints.limit if hints else None"},{"line_number":1838,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"6c6e863f_f79971c3","line":1835,"range":{"start_line":1830,"start_character":0,"end_line":1835,"end_character":62},"in_reply_to":"c78a0c93_2fedecf4","updated":"2026-05-11 17:34:36.000000000","message":"Done","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"6bec01f359e70259a421f18cdf2b693f881c0399","unresolved":true,"context_lines":[{"line_number":1839,"context_line":"        with self.get_connection() as conn:"},{"line_number":1840,"context_line":"            try:"},{"line_number":1841,"context_line":"                if conn.page_size and limit:"},{"line_number":1842,"context_line":"                    res \u003d conn._paged_search_s("},{"line_number":1843,"context_line":"                        self.tree_dn,"},{"line_number":1844,"context_line":"                        self.LDAP_SCOPE,"},{"line_number":1845,"context_line":"                        query,"},{"line_number":1846,"context_line":"                        attrs,"},{"line_number":1847,"context_line":"                        sizelimit\u003dlimit[\u0027limit\u0027],"},{"line_number":1848,"context_line":"                    )"},{"line_number":1849,"context_line":"                elif conn.page_size:"},{"line_number":1850,"context_line":"                    res \u003d conn._paged_search_s("},{"line_number":1851,"context_line":"                        self.tree_dn, self.LDAP_SCOPE, query, attrs"}],"source_content_type":"text/x-python","patch_set":11,"id":"e3c839b2_7f595094","line":1848,"range":{"start_line":1842,"start_character":0,"end_line":1848,"end_character":21},"updated":"2026-04-21 20:08:51.000000000","message":"this is not covered by unit tests. Please run tox -e cover to see what is covered and what is not.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"c2c068b731b6ccec0bdd0c94652cd29e45966370","unresolved":false,"context_lines":[{"line_number":1839,"context_line":"        with self.get_connection() as conn:"},{"line_number":1840,"context_line":"            try:"},{"line_number":1841,"context_line":"                if conn.page_size and limit:"},{"line_number":1842,"context_line":"                    res \u003d conn._paged_search_s("},{"line_number":1843,"context_line":"                        self.tree_dn,"},{"line_number":1844,"context_line":"                        self.LDAP_SCOPE,"},{"line_number":1845,"context_line":"                        query,"},{"line_number":1846,"context_line":"                        attrs,"},{"line_number":1847,"context_line":"                        sizelimit\u003dlimit[\u0027limit\u0027],"},{"line_number":1848,"context_line":"                    )"},{"line_number":1849,"context_line":"                elif conn.page_size:"},{"line_number":1850,"context_line":"                    res \u003d conn._paged_search_s("},{"line_number":1851,"context_line":"                        self.tree_dn, self.LDAP_SCOPE, query, attrs"}],"source_content_type":"text/x-python","patch_set":11,"id":"0bf7de5b_d885270d","line":1848,"range":{"start_line":1842,"start_character":0,"end_line":1848,"end_character":21},"in_reply_to":"e3c839b2_7f595094","updated":"2026-05-11 17:34:36.000000000","message":"Done","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":27900,"name":"Artem Goncharov","email":"artem.goncharov@gmail.com","username":"gtema"},"change_message_id":"c827d7e6ea6850721b7977335246777d999f6b4e","unresolved":true,"context_lines":[{"line_number":1165,"context_line":"            )"},{"line_number":1166,"context_line":"        else:"},{"line_number":1167,"context_line":"            try:"},{"line_number":1168,"context_line":"                ldap_result \u003d self.conn.search_s("},{"line_number":1169,"context_line":"                    base, scope, filterstr, attrlist, attrsonly"},{"line_number":1170,"context_line":"                )"},{"line_number":1171,"context_line":"            except ldap.SIZELIMIT_EXCEEDED:"}],"source_content_type":"text/x-python","patch_set":12,"id":"507507d6_b0e31e83","line":1168,"updated":"2026-05-15 10:05:51.000000000","message":"so if page_size is unset (or 0) we ignore the sizelimit completely simply returning whatever is there (potentially more). I guess when fixing an issue and passing the requested sizelimit into the function we should at least check we do not return more than requested","commit_id":"4e43ecfa215d253e6ca8641df9990085d1f0455d"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"f33bef9905f274bf4c235a82c567ce685612c94d","unresolved":false,"context_lines":[{"line_number":1165,"context_line":"            )"},{"line_number":1166,"context_line":"        else:"},{"line_number":1167,"context_line":"            try:"},{"line_number":1168,"context_line":"                ldap_result \u003d self.conn.search_s("},{"line_number":1169,"context_line":"                    base, scope, filterstr, attrlist, attrsonly"},{"line_number":1170,"context_line":"                )"},{"line_number":1171,"context_line":"            except ldap.SIZELIMIT_EXCEEDED:"}],"source_content_type":"text/x-python","patch_set":12,"id":"930af0d1_02d3b4b1","line":1168,"in_reply_to":"507507d6_b0e31e83","updated":"2026-05-15 12:23:38.000000000","message":"Done","commit_id":"4e43ecfa215d253e6ca8641df9990085d1f0455d"}],"keystone/tests/unit/fakeldap.py":[{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"6bec01f359e70259a421f18cdf2b693f881c0399","unresolved":true,"context_lines":[{"line_number":650,"context_line":"    def _get_paging_cookie(self, ctrl):"},{"line_number":651,"context_line":"        \"\"\"Extract the paging cookie from a control."},{"line_number":652,"context_line":""},{"line_number":653,"context_line":"        Supports both old (python-ldap \u003c 2.4) and new (\u003e\u003d 2.4) APIs."},{"line_number":654,"context_line":"        \"\"\""},{"line_number":655,"context_line":"        if hasattr(ctrl, \u0027cookie\u0027):"},{"line_number":656,"context_line":"            # New API (python-ldap \u003e\u003d 2.4)"}],"source_content_type":"text/x-python","patch_set":11,"id":"bec34f52_1b1b5e5a","line":653,"updated":"2026-04-21 20:08:51.000000000","message":"keystone depends on python-ldap\u003e\u003d3.0.0, this is not needed.","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"c2c068b731b6ccec0bdd0c94652cd29e45966370","unresolved":false,"context_lines":[{"line_number":650,"context_line":"    def _get_paging_cookie(self, ctrl):"},{"line_number":651,"context_line":"        \"\"\"Extract the paging cookie from a control."},{"line_number":652,"context_line":""},{"line_number":653,"context_line":"        Supports both old (python-ldap \u003c 2.4) and new (\u003e\u003d 2.4) APIs."},{"line_number":654,"context_line":"        \"\"\""},{"line_number":655,"context_line":"        if hasattr(ctrl, \u0027cookie\u0027):"},{"line_number":656,"context_line":"            # New API (python-ldap \u003e\u003d 2.4)"}],"source_content_type":"text/x-python","patch_set":11,"id":"c34f4cea_4084719e","line":653,"in_reply_to":"bec34f52_1b1b5e5a","updated":"2026-05-11 17:34:36.000000000","message":"Done","commit_id":"2eacd618b4669379759054099870aae7ce0902f5"},{"author":{"_account_id":27900,"name":"Artem Goncharov","email":"artem.goncharov@gmail.com","username":"gtema"},"change_message_id":"c827d7e6ea6850721b7977335246777d999f6b4e","unresolved":true,"context_lines":[{"line_number":627,"context_line":"        results \u003d self.search_s(*params[:5])"},{"line_number":628,"context_line":""},{"line_number":629,"context_line":"        serverctrls \u003d params[5]"},{"line_number":630,"context_line":"        ctrl \u003d serverctrls[0] if serverctrls else None"},{"line_number":631,"context_line":""},{"line_number":632,"context_line":"        if ctrl and hasattr(ctrl, \u0027size\u0027) and ctrl.size:"},{"line_number":633,"context_line":"            cookie \u003d self._get_paging_cookie(ctrl)"}],"source_content_type":"text/x-python","patch_set":12,"id":"d189b1fd_79ecf95c","line":630,"updated":"2026-05-15 10:05:51.000000000","message":"I see this handling of None and wondering, could None be returned by the real server in result3 function? Sadly pyldap docs are incredibly unspecific about that (https://www.python-ldap.org/en/python-ldap-3.4.6/reference/ldap.html#ldap.LDAPObject.result3). It is not even described what is the concrete type of the \"encoded server controls\". Current logic will throw an exception in this case, so maybe it would be good to extend the processing of result ensuring it is at least not None as well?","commit_id":"4e43ecfa215d253e6ca8641df9990085d1f0455d"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"f33bef9905f274bf4c235a82c567ce685612c94d","unresolved":false,"context_lines":[{"line_number":627,"context_line":"        results \u003d self.search_s(*params[:5])"},{"line_number":628,"context_line":""},{"line_number":629,"context_line":"        serverctrls \u003d params[5]"},{"line_number":630,"context_line":"        ctrl \u003d serverctrls[0] if serverctrls else None"},{"line_number":631,"context_line":""},{"line_number":632,"context_line":"        if ctrl and hasattr(ctrl, \u0027size\u0027) and ctrl.size:"},{"line_number":633,"context_line":"            cookie \u003d self._get_paging_cookie(ctrl)"}],"source_content_type":"text/x-python","patch_set":12,"id":"25cca6ba_9319a6f9","line":630,"in_reply_to":"d189b1fd_79ecf95c","updated":"2026-05-15 12:23:38.000000000","message":"The docs are really vague there, so I went ahead and guarded both`rdata` and `serverctrls` with or `[]` in `_paged_search_s` in `common.py` where `result3` is actually called.","commit_id":"4e43ecfa215d253e6ca8641df9990085d1f0455d"}],"keystone/tests/unit/identity/backends/test_ldap_common.py":[{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"3c77b78639e48871ff55e2d38a9982a3a18bff3c","unresolved":false,"context_lines":[{"line_number":16,"context_line":"import uuid"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"import fixtures"},{"line_number":19,"context_line":"import keystone.conf"},{"line_number":20,"context_line":"import ldap.dn"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"from keystone.common import driver_hints"}],"source_content_type":"text/x-python","patch_set":3,"id":"f1b0d1e9_c24bbd5f","line":19,"updated":"2026-04-01 18:30:27.000000000","message":"Same unrelated import reordering as in common.py.","commit_id":"79c6ded05cee425938e664903f0035a1bf7a7b65"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"3c77b78639e48871ff55e2d38a9982a3a18bff3c","unresolved":false,"context_lines":[{"line_number":460,"context_line":"        attrlist \u003d sorted([attr for attr in args[3] if attr])"},{"line_number":461,"context_line":"        self.assertEqual([\u0027mail\u0027, \u0027userPassword\u0027], attrlist)"},{"line_number":462,"context_line":""},{"line_number":463,"context_line":"    @mock.patch.object(fakeldap.FakeLdap, \u0027search_ext\u0027)"},{"line_number":464,"context_line":"    @mock.patch.object(fakeldap.FakeLdap, \u0027result3\u0027)"},{"line_number":465,"context_line":"    def test_paged_results_handles_none_serverctrls("},{"line_number":466,"context_line":"        self, mock_result3, mock_search_ext"}],"source_content_type":"text/x-python","patch_set":3,"id":"871f3259_5fccd188","line":463,"updated":"2026-04-01 18:30:27.000000000","message":"This test covers the secondary serverctrls-is-None fix, but there is no test for the primary pagination bug: that _ldap_get_all returns more than page_size results when hints.limit is set and hints.filters is empty. A test should set up a fake LDAP with entries exceeding page_size, set hints.limit, and verify all entries are returned.","commit_id":"79c6ded05cee425938e664903f0035a1bf7a7b65"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"59069af7410962df9b97e7f102b22d8423098216","unresolved":true,"context_lines":[{"line_number":476,"context_line":"            extra_users.append(user)"},{"line_number":477,"context_line":""},{"line_number":478,"context_line":"        # page_size\u003d2 forces multiple LDAP pages to be fetched"},{"line_number":479,"context_line":"        self.config_fixture.config(group\u003d\u0027ldap\u0027, page_size\u003d2)"},{"line_number":480,"context_line":""},{"line_number":481,"context_line":"        users \u003d PROVIDERS.identity_api.list_users()"},{"line_number":482,"context_line":""},{"line_number":483,"context_line":"        # All default fixture users plus the extra ones must be present"},{"line_number":484,"context_line":"        expected_count \u003d len(default_fixtures.USERS) + len(extra_users)"}],"source_content_type":"text/x-python","patch_set":9,"id":"bea0c420_4d1cb7f5","line":481,"range":{"start_line":479,"start_character":0,"end_line":481,"end_character":51},"updated":"2026-04-17 15:35:53.000000000","message":"I have doubts about this. The test does not demonstrate that your call actually goes through _paged_search_s that you change. It might be that the call goes through the search_s, without calling the pagination.\n\nPlease change the test so that it demonstrated that _paged_search_s is actually called.","commit_id":"9c4943e3625bc7a6f25f23fe531e490bfe7b2225"},{"author":{"_account_id":38589,"name":"Moutaz Chaara","display_name":"Moutaz Chaara","email":"moutaz.chaara@sap.com","username":"tz3","status":"SAP SE"},"change_message_id":"c2c068b731b6ccec0bdd0c94652cd29e45966370","unresolved":false,"context_lines":[{"line_number":476,"context_line":"            extra_users.append(user)"},{"line_number":477,"context_line":""},{"line_number":478,"context_line":"        # page_size\u003d2 forces multiple LDAP pages to be fetched"},{"line_number":479,"context_line":"        self.config_fixture.config(group\u003d\u0027ldap\u0027, page_size\u003d2)"},{"line_number":480,"context_line":""},{"line_number":481,"context_line":"        users \u003d PROVIDERS.identity_api.list_users()"},{"line_number":482,"context_line":""},{"line_number":483,"context_line":"        # All default fixture users plus the extra ones must be present"},{"line_number":484,"context_line":"        expected_count \u003d len(default_fixtures.USERS) + len(extra_users)"}],"source_content_type":"text/x-python","patch_set":9,"id":"04c83784_5cd3a9be","line":481,"range":{"start_line":479,"start_character":0,"end_line":481,"end_character":51},"in_reply_to":"bea0c420_4d1cb7f5","updated":"2026-05-11 17:34:36.000000000","message":"Done","commit_id":"9c4943e3625bc7a6f25f23fe531e490bfe7b2225"}]}
