)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"efa59caad228ea6064c8002f26ae62463105e952","unresolved":true,"context_lines":[{"line_number":34,"context_line":"To activited :"},{"line_number":35,"context_line":"OPENSTACK_KEYSTONE_MFA_TOTP_ENABLED \u003d True"},{"line_number":36,"context_line":"and an user with a role in OPENSTACK_MFA_ROLES"},{"line_number":37,"context_line":"exemple: OPENSTACK_MFA_ROLES \u003d [ \u0027admin\u0027, \u0027mfa-role\u0027]"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"Closes-Bug: #2142657"},{"line_number":40,"context_line":"Blueprint: #enabling-mfa-dashboard"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":10,"id":"44378719_b6a4bd84","line":37,"updated":"2026-03-10 13:09:27.000000000","message":"```suggestion\nexample: OPENSTACK_MFA_ROLES \u003d [ \u0027admin\u0027, \u0027mfa-role\u0027]\n```","commit_id":"d8a9f4207b15382f388c6cf636c3e1338e44006d"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"7211aea7272b3619c56951b39dbbc0ce648ecb35","unresolved":false,"context_lines":[{"line_number":34,"context_line":"To activited :"},{"line_number":35,"context_line":"OPENSTACK_KEYSTONE_MFA_TOTP_ENABLED \u003d True"},{"line_number":36,"context_line":"and an user with a role in OPENSTACK_MFA_ROLES"},{"line_number":37,"context_line":"exemple: OPENSTACK_MFA_ROLES \u003d [ \u0027admin\u0027, \u0027mfa-role\u0027]"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"Closes-Bug: #2142657"},{"line_number":40,"context_line":"Blueprint: #enabling-mfa-dashboard"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":10,"id":"dc1540e8_e588f91f","line":37,"in_reply_to":"44378719_b6a4bd84","updated":"2026-03-10 13:22:39.000000000","message":"Done","commit_id":"d8a9f4207b15382f388c6cf636c3e1338e44006d"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"0b13d149f906ff0dee4aba11372e7fb72662e4dd","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":1,"id":"1f68b7ab_66733849","updated":"2026-02-25 12:01:31.000000000","message":"Hello,\n\nPlease add release note.","commit_id":"b5df04b4f699dae8927b382ca4d60789cd7bb858"},{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"d06a9de429d1504560bf9c6076ddd7934456da24","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"44e2b285_1bd7bbfc","in_reply_to":"1f68b7ab_66733849","updated":"2026-02-25 15:46:04.000000000","message":"https://review.opendev.org/c/openstack/horizon/+/977978?usp\u003dsearch","commit_id":"b5df04b4f699dae8927b382ca4d60789cd7bb858"},{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"939c4c4ac1502a196d2003eb2cba5f0d322b163a","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"1878cd91_b8c7008d","updated":"2026-02-25 16:04:50.000000000","message":"Please add support translation in JS files.","commit_id":"1b1bf574379f132759adc41493b1c038844a4fef"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"635b4dcf768364e23cb496af4f7ba94a76f0a41a","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"d858ad8f_363e2488","in_reply_to":"1878cd91_b8c7008d","updated":"2026-03-06 15:33:05.000000000","message":"I removed the JS files to have a code architecture that is as similar as possible to the rest of the project.","commit_id":"1b1bf574379f132759adc41493b1c038844a4fef"},{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"335afe120d3327e7658f3a198bfe00394e5e2ece","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"fc30cf0f_769cacae","in_reply_to":"d858ad8f_363e2488","updated":"2026-03-10 13:07:48.000000000","message":"Done","commit_id":"1b1bf574379f132759adc41493b1c038844a4fef"},{"author":{"_account_id":8648,"name":"Radomir Dopieralski","email":"openstack@dopieralski.pl","username":"thesheep"},"change_message_id":"1091f21b6444ad51d6d4c499de9c455d603202fe","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":16,"id":"fe2a7d14_eeb1f6d1","updated":"2026-04-09 15:03:24.000000000","message":"Some comments I have by just looking at the code. I\u0027m going to test it some more, and probably have more notes. Thank you for working on it.","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"}],"doc/source/configuration/settings.rst":[{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"2a4c34fc6e07b5d038c6aacc851ee520b40da556","unresolved":true,"context_lines":[{"line_number":652,"context_line":"be present in ``AUTHENTICATION_PLUGINS``."},{"line_number":653,"context_line":""},{"line_number":654,"context_line":"OPENSTACK_MFA_ROLES"},{"line_number":655,"context_line":"-----------------------------------"},{"line_number":656,"context_line":""},{"line_number":657,"context_line":".. versionadded:: ..."},{"line_number":658,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"1dc5a441_5e177c8a","line":655,"updated":"2026-03-17 10:48:52.000000000","message":"```suggestion\n-------------------\n```","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"6a1bba05e16e17c743bf15f2a3e416d07b02a637","unresolved":false,"context_lines":[{"line_number":652,"context_line":"be present in ``AUTHENTICATION_PLUGINS``."},{"line_number":653,"context_line":""},{"line_number":654,"context_line":"OPENSTACK_MFA_ROLES"},{"line_number":655,"context_line":"-----------------------------------"},{"line_number":656,"context_line":""},{"line_number":657,"context_line":".. versionadded:: ..."},{"line_number":658,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"23cc0e28_eb6cd386","line":655,"in_reply_to":"1dc5a441_5e177c8a","updated":"2026-03-17 11:14:26.000000000","message":"Done","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"883b30a953b375f26fbbc82610579808bd9f0b08","unresolved":false,"context_lines":[{"line_number":652,"context_line":"be present in ``AUTHENTICATION_PLUGINS``."},{"line_number":653,"context_line":""},{"line_number":654,"context_line":"OPENSTACK_MFA_ROLES"},{"line_number":655,"context_line":"-----------------------------------"},{"line_number":656,"context_line":""},{"line_number":657,"context_line":".. versionadded:: ..."},{"line_number":658,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"d428d66d_f65df49b","line":655,"in_reply_to":"23cc0e28_eb6cd386","updated":"2026-03-17 11:18:01.000000000","message":"Patchset 16*","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"2a4c34fc6e07b5d038c6aacc851ee520b40da556","unresolved":true,"context_lines":[{"line_number":654,"context_line":"OPENSTACK_MFA_ROLES"},{"line_number":655,"context_line":"-----------------------------------"},{"line_number":656,"context_line":""},{"line_number":657,"context_line":".. versionadded:: ..."},{"line_number":658,"context_line":""},{"line_number":659,"context_line":"Default: ``[\u0027admin\u0027]``"},{"line_number":660,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"a48e234d_6a13eaca","line":657,"updated":"2026-03-17 10:48:52.000000000","message":"Already 2026.2","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"6a1bba05e16e17c743bf15f2a3e416d07b02a637","unresolved":false,"context_lines":[{"line_number":654,"context_line":"OPENSTACK_MFA_ROLES"},{"line_number":655,"context_line":"-----------------------------------"},{"line_number":656,"context_line":""},{"line_number":657,"context_line":".. versionadded:: ..."},{"line_number":658,"context_line":""},{"line_number":659,"context_line":"Default: ``[\u0027admin\u0027]``"},{"line_number":660,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"b2c963e1_951d7f3c","line":657,"in_reply_to":"a48e234d_6a13eaca","updated":"2026-03-17 11:14:26.000000000","message":"Done","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"883b30a953b375f26fbbc82610579808bd9f0b08","unresolved":false,"context_lines":[{"line_number":654,"context_line":"OPENSTACK_MFA_ROLES"},{"line_number":655,"context_line":"-----------------------------------"},{"line_number":656,"context_line":""},{"line_number":657,"context_line":".. versionadded:: ..."},{"line_number":658,"context_line":""},{"line_number":659,"context_line":"Default: ``[\u0027admin\u0027]``"},{"line_number":660,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"ff6d3733_4444082d","line":657,"in_reply_to":"b2c963e1_951d7f3c","updated":"2026-03-17 11:18:01.000000000","message":"Patchset 16*","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"2a4c34fc6e07b5d038c6aacc851ee520b40da556","unresolved":true,"context_lines":[{"line_number":656,"context_line":""},{"line_number":657,"context_line":".. versionadded:: ..."},{"line_number":658,"context_line":""},{"line_number":659,"context_line":"Default: ``[\u0027admin\u0027]``"},{"line_number":660,"context_line":""},{"line_number":661,"context_line":"List of roles that are allowed to activate Multi-Factor Authentication (MFA)."},{"line_number":662,"context_line":"Users with one of these roles can enable MFA using password + TOTP"}],"source_content_type":"text/x-rst","patch_set":15,"id":"97734ded_d15e0919","line":659,"updated":"2026-03-17 10:48:52.000000000","message":"Hmm, may be member too?","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"d5ea6f6ecb21c086216cb8e883f0926157bc8549","unresolved":false,"context_lines":[{"line_number":656,"context_line":""},{"line_number":657,"context_line":".. versionadded:: ..."},{"line_number":658,"context_line":""},{"line_number":659,"context_line":"Default: ``[\u0027admin\u0027]``"},{"line_number":660,"context_line":""},{"line_number":661,"context_line":"List of roles that are allowed to activate Multi-Factor Authentication (MFA)."},{"line_number":662,"context_line":"Users with one of these roles can enable MFA using password + TOTP"}],"source_content_type":"text/x-rst","patch_set":15,"id":"b05ba593_43c92c9c","line":659,"in_reply_to":"77232752_acf166fe","updated":"2026-03-25 15:03:25.000000000","message":"Done","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"6a1bba05e16e17c743bf15f2a3e416d07b02a637","unresolved":true,"context_lines":[{"line_number":656,"context_line":""},{"line_number":657,"context_line":".. versionadded:: ..."},{"line_number":658,"context_line":""},{"line_number":659,"context_line":"Default: ``[\u0027admin\u0027]``"},{"line_number":660,"context_line":""},{"line_number":661,"context_line":"List of roles that are allowed to activate Multi-Factor Authentication (MFA)."},{"line_number":662,"context_line":"Users with one of these roles can enable MFA using password + TOTP"}],"source_content_type":"text/x-rst","patch_set":15,"id":"77232752_acf166fe","line":659,"in_reply_to":"97734ded_d15e0919","updated":"2026-03-17 11:14:26.000000000","message":"By default, I only assign the “admin” role, because for the dashboard to function properly, the user must have the necessary role to modify the options --enable-multi-factor-auth and --multi-factor-auth-rule and view their TOTP credentials, and by default in Keystone, only users with the “admin” role have this permission.","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"2a4c34fc6e07b5d038c6aacc851ee520b40da556","unresolved":true,"context_lines":[{"line_number":663,"context_line":"authentication."},{"line_number":664,"context_line":""},{"line_number":665,"context_line":"OPENSTACK_TOTP_QRCODE_ISSUER"},{"line_number":666,"context_line":"-----------------------------------"},{"line_number":667,"context_line":""},{"line_number":668,"context_line":".. versionadded:: ..."},{"line_number":669,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"6b929de5_564eaee2","line":666,"updated":"2026-03-17 10:48:52.000000000","message":"```suggestion\n----------------------------\n```","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"6a1bba05e16e17c743bf15f2a3e416d07b02a637","unresolved":false,"context_lines":[{"line_number":663,"context_line":"authentication."},{"line_number":664,"context_line":""},{"line_number":665,"context_line":"OPENSTACK_TOTP_QRCODE_ISSUER"},{"line_number":666,"context_line":"-----------------------------------"},{"line_number":667,"context_line":""},{"line_number":668,"context_line":".. versionadded:: ..."},{"line_number":669,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"ec14dabf_84116e88","line":666,"in_reply_to":"6b929de5_564eaee2","updated":"2026-03-17 11:14:26.000000000","message":"Done","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"883b30a953b375f26fbbc82610579808bd9f0b08","unresolved":false,"context_lines":[{"line_number":663,"context_line":"authentication."},{"line_number":664,"context_line":""},{"line_number":665,"context_line":"OPENSTACK_TOTP_QRCODE_ISSUER"},{"line_number":666,"context_line":"-----------------------------------"},{"line_number":667,"context_line":""},{"line_number":668,"context_line":".. versionadded:: ..."},{"line_number":669,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"b8837df7_9ea664ed","line":666,"in_reply_to":"ec14dabf_84116e88","updated":"2026-03-17 11:18:01.000000000","message":"Patchset 16*","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"2a4c34fc6e07b5d038c6aacc851ee520b40da556","unresolved":true,"context_lines":[{"line_number":665,"context_line":"OPENSTACK_TOTP_QRCODE_ISSUER"},{"line_number":666,"context_line":"-----------------------------------"},{"line_number":667,"context_line":""},{"line_number":668,"context_line":".. versionadded:: ..."},{"line_number":669,"context_line":""},{"line_number":670,"context_line":"Default: ``OpenStack``"},{"line_number":671,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"89a098f2_8c2d1580","line":668,"updated":"2026-03-17 10:48:52.000000000","message":"Already 2026.2","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"6a1bba05e16e17c743bf15f2a3e416d07b02a637","unresolved":false,"context_lines":[{"line_number":665,"context_line":"OPENSTACK_TOTP_QRCODE_ISSUER"},{"line_number":666,"context_line":"-----------------------------------"},{"line_number":667,"context_line":""},{"line_number":668,"context_line":".. versionadded:: ..."},{"line_number":669,"context_line":""},{"line_number":670,"context_line":"Default: ``OpenStack``"},{"line_number":671,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"a7ef5d39_02f2dd1c","line":668,"in_reply_to":"89a098f2_8c2d1580","updated":"2026-03-17 11:14:26.000000000","message":"Done","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"883b30a953b375f26fbbc82610579808bd9f0b08","unresolved":false,"context_lines":[{"line_number":665,"context_line":"OPENSTACK_TOTP_QRCODE_ISSUER"},{"line_number":666,"context_line":"-----------------------------------"},{"line_number":667,"context_line":""},{"line_number":668,"context_line":".. versionadded:: ..."},{"line_number":669,"context_line":""},{"line_number":670,"context_line":"Default: ``OpenStack``"},{"line_number":671,"context_line":""}],"source_content_type":"text/x-rst","patch_set":15,"id":"bd78604f_88d89fc8","line":668,"in_reply_to":"a7ef5d39_02f2dd1c","updated":"2026-03-17 11:18:01.000000000","message":"Patchset 16*","commit_id":"53c90c28dac40b48df9acbcf5c368961d59110aa"}],"openstack_auth/urls.py":[{"author":{"_account_id":37598,"name":"Ivan Anfimov","display_name":"Ivan Anfimov","email":"lazekteam@gmail.com","username":"anfimovir"},"change_message_id":"a9083c2f0cde3546efd65b89fb03a99968cc3a8f","unresolved":true,"context_lines":[{"line_number":35,"context_line":"            name\u003d\u0027switch_system_scope\u0027),"},{"line_number":36,"context_line":"]"},{"line_number":37,"context_line":""},{"line_number":38,"context_line":"if utils.allow_expired_password_change():"},{"line_number":39,"context_line":"    urlpatterns.append("},{"line_number":40,"context_line":"        re_path(r\u0027^password/(?P\u003cuser_id\u003e[^/]+)/$\u0027,"},{"line_number":41,"context_line":"                views.PasswordView.as_view(),"}],"source_content_type":"text/x-python","patch_set":4,"id":"11dc7ef3_8963f61d","line":38,"updated":"2026-02-26 13:13:41.000000000","message":"Fixed in another MR.. Please rebase your code to latest version and remove different changes.","commit_id":"e22a17cb3e314abf78bf55b044918ea55e9dc8d8"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"635b4dcf768364e23cb496af4f7ba94a76f0a41a","unresolved":false,"context_lines":[{"line_number":35,"context_line":"            name\u003d\u0027switch_system_scope\u0027),"},{"line_number":36,"context_line":"]"},{"line_number":37,"context_line":""},{"line_number":38,"context_line":"if utils.allow_expired_password_change():"},{"line_number":39,"context_line":"    urlpatterns.append("},{"line_number":40,"context_line":"        re_path(r\u0027^password/(?P\u003cuser_id\u003e[^/]+)/$\u0027,"},{"line_number":41,"context_line":"                views.PasswordView.as_view(),"}],"source_content_type":"text/x-python","patch_set":4,"id":"c4cd5e0c_9d0dc216","line":38,"in_reply_to":"11dc7ef3_8963f61d","updated":"2026-03-06 15:33:05.000000000","message":"Resolved","commit_id":"e22a17cb3e314abf78bf55b044918ea55e9dc8d8"}],"openstack_dashboard/api/keystone.py":[{"author":{"_account_id":8648,"name":"Radomir Dopieralski","email":"openstack@dopieralski.pl","username":"thesheep"},"change_message_id":"1091f21b6444ad51d6d4c499de9c455d603202fe","unresolved":true,"context_lines":[{"line_number":516,"context_line":"    current_options \u003d getattr(user, \u0027options\u0027, {}).copy()"},{"line_number":517,"context_line":"    current_options.update(options)"},{"line_number":518,"context_line":"    client \u003d keystoneclient(request, admin\u003dFalse)"},{"line_number":519,"context_line":"    return client.users.update(user_id, options\u003dcurrent_options)"},{"line_number":520,"context_line":""},{"line_number":521,"context_line":""},{"line_number":522,"context_line":"@profiler.trace"}],"source_content_type":"text/x-python","patch_set":16,"id":"5c99d560_f00e8f38","line":519,"updated":"2026-04-09 15:03:24.000000000","message":"Can this function just call the previous function, instead of duplicating its code? Something like:\n\n```python\ndef user_update_own_options(request, **options):\n  return user_update_options(request, request.user.id, **options)\n```","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"e8dbee8e533f253a4b21a164bb6d19088de42930","unresolved":true,"context_lines":[{"line_number":516,"context_line":"    current_options \u003d getattr(user, \u0027options\u0027, {}).copy()"},{"line_number":517,"context_line":"    current_options.update(options)"},{"line_number":518,"context_line":"    client \u003d keystoneclient(request, admin\u003dFalse)"},{"line_number":519,"context_line":"    return client.users.update(user_id, options\u003dcurrent_options)"},{"line_number":520,"context_line":""},{"line_number":521,"context_line":""},{"line_number":522,"context_line":"@profiler.trace"}],"source_content_type":"text/x-python","patch_set":16,"id":"ec63c272_86c90cdb","line":519,"in_reply_to":"5c99d560_f00e8f38","updated":"2026-04-10 09:39:25.000000000","message":"Good point, I\u0027ll look into that. I just need to be careful with admin permissions. We don\u0027t want users to be blocked from changing their own options.","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"bb2be9fc7fd9050a2673842547c10fa26637d5cb","unresolved":false,"context_lines":[{"line_number":516,"context_line":"    current_options \u003d getattr(user, \u0027options\u0027, {}).copy()"},{"line_number":517,"context_line":"    current_options.update(options)"},{"line_number":518,"context_line":"    client \u003d keystoneclient(request, admin\u003dFalse)"},{"line_number":519,"context_line":"    return client.users.update(user_id, options\u003dcurrent_options)"},{"line_number":520,"context_line":""},{"line_number":521,"context_line":""},{"line_number":522,"context_line":"@profiler.trace"}],"source_content_type":"text/x-python","patch_set":16,"id":"474225df_a1e7dd02","line":519,"in_reply_to":"ec63c272_86c90cdb","updated":"2026-05-28 12:34:31.000000000","message":"Done","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"}],"openstack_dashboard/dashboards/settings/mfa/panel.py":[{"author":{"_account_id":8648,"name":"Radomir Dopieralski","email":"openstack@dopieralski.pl","username":"thesheep"},"change_message_id":"1091f21b6444ad51d6d4c499de9c455d603202fe","unresolved":true,"context_lines":[{"line_number":29,"context_line":"    def allowed(self, context):"},{"line_number":30,"context_line":"        request \u003d context[\u0027request\u0027]"},{"line_number":31,"context_line":"        allowed_roles \u003d settings.OPENSTACK_MFA_ROLES"},{"line_number":32,"context_line":"        user_roles \u003d [role[\u0027name\u0027] for role in request.user.roles]"},{"line_number":33,"context_line":"        if request.user.is_superuser:"},{"line_number":34,"context_line":"            return True"},{"line_number":35,"context_line":"        return any(role in user_roles for role in allowed_roles)"}],"source_content_type":"text/x-python","patch_set":16,"id":"0d7d5ff0_f8a7335d","line":32,"updated":"2026-04-09 15:03:24.000000000","message":"should this be based on policy checks, rather than a separate setting?","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"e8dbee8e533f253a4b21a164bb6d19088de42930","unresolved":true,"context_lines":[{"line_number":29,"context_line":"    def allowed(self, context):"},{"line_number":30,"context_line":"        request \u003d context[\u0027request\u0027]"},{"line_number":31,"context_line":"        allowed_roles \u003d settings.OPENSTACK_MFA_ROLES"},{"line_number":32,"context_line":"        user_roles \u003d [role[\u0027name\u0027] for role in request.user.roles]"},{"line_number":33,"context_line":"        if request.user.is_superuser:"},{"line_number":34,"context_line":"            return True"},{"line_number":35,"context_line":"        return any(role in user_roles for role in allowed_roles)"}],"source_content_type":"text/x-python","patch_set":16,"id":"fd4a2699_e8bc13b6","line":32,"in_reply_to":"0d7d5ff0_f8a7335d","updated":"2026-04-10 09:39:25.000000000","message":"I think you\u0027ve got a better lead. I\u0027ll take a look at it.","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"bb2be9fc7fd9050a2673842547c10fa26637d5cb","unresolved":false,"context_lines":[{"line_number":29,"context_line":"    def allowed(self, context):"},{"line_number":30,"context_line":"        request \u003d context[\u0027request\u0027]"},{"line_number":31,"context_line":"        allowed_roles \u003d settings.OPENSTACK_MFA_ROLES"},{"line_number":32,"context_line":"        user_roles \u003d [role[\u0027name\u0027] for role in request.user.roles]"},{"line_number":33,"context_line":"        if request.user.is_superuser:"},{"line_number":34,"context_line":"            return True"},{"line_number":35,"context_line":"        return any(role in user_roles for role in allowed_roles)"}],"source_content_type":"text/x-python","patch_set":16,"id":"df68bce5_b95e2bde","line":32,"in_reply_to":"fd4a2699_e8bc13b6","updated":"2026-05-28 12:34:31.000000000","message":"Done","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"}],"openstack_dashboard/dashboards/settings/mfa/utils.py":[{"author":{"_account_id":8648,"name":"Radomir Dopieralski","email":"openstack@dopieralski.pl","username":"thesheep"},"change_message_id":"1091f21b6444ad51d6d4c499de9c455d603202fe","unresolved":true,"context_lines":[{"line_number":14,"context_line":""},{"line_number":15,"context_line":"from cryptography.hazmat.backends import default_backend"},{"line_number":16,"context_line":"from cryptography.hazmat.primitives import hashes"},{"line_number":17,"context_line":"from cryptography.hazmat.primitives.twofactor import totp as crypto_totp"},{"line_number":18,"context_line":"from oslo_utils import timeutils"},{"line_number":19,"context_line":""},{"line_number":20,"context_line":""}],"source_content_type":"text/x-python","patch_set":16,"id":"c2e6adcf_e3a98f5c","line":17,"updated":"2026-04-09 15:03:24.000000000","message":"do we need to add this library to requirements.txt?","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"bb2be9fc7fd9050a2673842547c10fa26637d5cb","unresolved":false,"context_lines":[{"line_number":14,"context_line":""},{"line_number":15,"context_line":"from cryptography.hazmat.backends import default_backend"},{"line_number":16,"context_line":"from cryptography.hazmat.primitives import hashes"},{"line_number":17,"context_line":"from cryptography.hazmat.primitives.twofactor import totp as crypto_totp"},{"line_number":18,"context_line":"from oslo_utils import timeutils"},{"line_number":19,"context_line":""},{"line_number":20,"context_line":""}],"source_content_type":"text/x-python","patch_set":16,"id":"138fa194_f503c6c9","line":17,"in_reply_to":"197673a9_dd7c7802","updated":"2026-05-28 12:34:31.000000000","message":"Done","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"e8dbee8e533f253a4b21a164bb6d19088de42930","unresolved":true,"context_lines":[{"line_number":14,"context_line":""},{"line_number":15,"context_line":"from cryptography.hazmat.backends import default_backend"},{"line_number":16,"context_line":"from cryptography.hazmat.primitives import hashes"},{"line_number":17,"context_line":"from cryptography.hazmat.primitives.twofactor import totp as crypto_totp"},{"line_number":18,"context_line":"from oslo_utils import timeutils"},{"line_number":19,"context_line":""},{"line_number":20,"context_line":""}],"source_content_type":"text/x-python","patch_set":16,"id":"197673a9_dd7c7802","line":17,"in_reply_to":"c2e6adcf_e3a98f5c","updated":"2026-04-10 09:39:25.000000000","message":"I drew heavily on the totp.py plugin from the Keystone project: keystone/auth/plugins/totp.py and it is listed in the requirements: \n\nhttps://opendev.org/openstack/keystone/src/branch/master/requirements.txt#:~:text\u003dcryptography%3E%3D2%2E7%20%23%20BSD%2FApache%2D2%2E0\n\nI\u0027ll add it","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"}],"openstack_dashboard/dashboards/settings/mfa/workflows.py":[{"author":{"_account_id":8648,"name":"Radomir Dopieralski","email":"openstack@dopieralski.pl","username":"thesheep"},"change_message_id":"1091f21b6444ad51d6d4c499de9c455d603202fe","unresolved":true,"context_lines":[{"line_number":42,"context_line":"            \"FreeOTP, etc.).\\n\""},{"line_number":43,"context_line":"            \"- Save the QR code or secret key before continuing.\\n\""},{"line_number":44,"context_line":"            \"- If you lose access to your authenticator, you may be locked \""},{"line_number":45,"context_line":"            \"out of your account.\""},{"line_number":46,"context_line":"        )"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    def handle(self, request, context):"}],"source_content_type":"text/x-python","patch_set":16,"id":"31dcac57_f0828815","line":45,"updated":"2026-04-09 15:03:24.000000000","message":"perhaps a multi-line string (with \"\"\") would work here better","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"e8dbee8e533f253a4b21a164bb6d19088de42930","unresolved":true,"context_lines":[{"line_number":42,"context_line":"            \"FreeOTP, etc.).\\n\""},{"line_number":43,"context_line":"            \"- Save the QR code or secret key before continuing.\\n\""},{"line_number":44,"context_line":"            \"- If you lose access to your authenticator, you may be locked \""},{"line_number":45,"context_line":"            \"out of your account.\""},{"line_number":46,"context_line":"        )"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    def handle(self, request, context):"}],"source_content_type":"text/x-python","patch_set":16,"id":"877e33b0_90769260","line":45,"in_reply_to":"31dcac57_f0828815","updated":"2026-04-10 09:39:25.000000000","message":"We don’t typically use multi-line triple-quoted strings for help_text in Horizon.\nAlso, indentation in \"\"\" strings may introduce unintended whitespace, so I used the \\n format for consistency and to avoid formatting issues.","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"6f1ed42f85b28152f27d121c4c17927b5b1b0bd0","unresolved":false,"context_lines":[{"line_number":42,"context_line":"            \"FreeOTP, etc.).\\n\""},{"line_number":43,"context_line":"            \"- Save the QR code or secret key before continuing.\\n\""},{"line_number":44,"context_line":"            \"- If you lose access to your authenticator, you may be locked \""},{"line_number":45,"context_line":"            \"out of your account.\""},{"line_number":46,"context_line":"        )"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    def handle(self, request, context):"}],"source_content_type":"text/x-python","patch_set":16,"id":"d471bd32_fba7cc74","line":45,"in_reply_to":"877e33b0_90769260","updated":"2026-05-28 12:53:14.000000000","message":"Done","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"}],"releasenotes/notes/enable-mfa-from-horizon-d53fd6f4453b2abb.yaml":[{"author":{"_account_id":8648,"name":"Radomir Dopieralski","email":"openstack@dopieralski.pl","username":"thesheep"},"change_message_id":"1091f21b6444ad51d6d4c499de9c455d603202fe","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":16,"id":"98775e57_51859381","line":14,"updated":"2026-04-09 15:03:24.000000000","message":"Would be nice to also mention the new settings here.","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"},{"author":{"_account_id":36012,"name":"Benjamin Lasseye","display_name":"blasseye","email":"blasseye@ikmail.com","username":"blasseye"},"change_message_id":"bb2be9fc7fd9050a2673842547c10fa26637d5cb","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":16,"id":"3ef0a294_6c65db17","line":14,"in_reply_to":"98775e57_51859381","updated":"2026-05-28 12:34:31.000000000","message":"Done","commit_id":"388a36d679d218b0fe0a2fd1566b122493cf82e3"}]}
