)]}'
{"keystone/common/config.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"1589ab6b2abca5bf8891fb4661137bdb3129a95d","unresolved":false,"context_lines":[{"line_number":1054,"context_line":"                        \u0027before the account becomes disabled. Setting the \u0027"},{"line_number":1055,"context_line":"                        \u0027value to 0 disables this feature. Note: this \u0027"},{"line_number":1056,"context_line":"                        \u0027feature is only supported via the SQL backend \u0027"},{"line_number":1057,"context_line":"                        \u0027driver for identity.\u0027),"},{"line_number":1058,"context_line":"        cfg.IntOpt(\u0027lockout_failure_attempts\u0027,"},{"line_number":1059,"context_line":"                   default\u003d0,"},{"line_number":1060,"context_line":"                   help\u003d\u0027Number of times a user can fail login attempts until \u0027"}],"source_content_type":"text/x-python","patch_set":15,"id":"7aa08908_f62694b8","line":1057,"updated":"2016-06-13 16:03:47.000000000","message":"nit: this could probably be done by the PCI documentation we\u0027ll add but we could say that equivalents for this in LDAP will be specific to the LDAP implementation you choose to integrate with keystone.","commit_id":"22921f15ba52fe0b88c206b9adec1a04491aa516"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c563544049b48825d021288e40a0d21ebabd5eb6","unresolved":false,"context_lines":[{"line_number":1049,"context_line":"    ],"},{"line_number":1050,"context_line":"    \u0027security_compliance\u0027: ["},{"line_number":1051,"context_line":"        cfg.IntOpt(\u0027disable_user_account_days_inactive\u0027,"},{"line_number":1052,"context_line":"                   default\u003d0,"},{"line_number":1053,"context_line":"                   help\u003d\u0027Number of days for which a user can be inactive \u0027"},{"line_number":1054,"context_line":"                        \u0027before the account becomes disabled. Setting the \u0027"},{"line_number":1055,"context_line":"                        \u0027value to 0 disables this feature.\u0027),"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_ef9a8061","side":"PARENT","line":1052,"updated":"2016-06-16 16:41:36.000000000","message":"This should default to None, and have a min\u003d1 to force a user to login every day.","commit_id":"7794057246753f6ceb57d7dab1b7275a32e082a0"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"d73bb5f7e94a1d0eaf50ed86a1a0cc6c950ba03c","unresolved":false,"context_lines":[{"line_number":1049,"context_line":"    ],"},{"line_number":1050,"context_line":"    \u0027security_compliance\u0027: ["},{"line_number":1051,"context_line":"        cfg.IntOpt(\u0027disable_user_account_days_inactive\u0027,"},{"line_number":1052,"context_line":"                   default\u003d0,"},{"line_number":1053,"context_line":"                   help\u003d\u0027Number of days for which a user can be inactive \u0027"},{"line_number":1054,"context_line":"                        \u0027before the account becomes disabled. Setting the \u0027"},{"line_number":1055,"context_line":"                        \u0027value to 0 disables this feature.\u0027),"}],"source_content_type":"text/x-python","patch_set":29,"id":"3aaa91ec_f896e892","side":"PARENT","line":1052,"in_reply_to":"7aa08908_ef9a8061","updated":"2016-06-22 16:56:34.000000000","message":"Done","commit_id":"7794057246753f6ceb57d7dab1b7275a32e082a0"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"7cd215155d940f4e8c6957e7f95678fe9701e273","unresolved":false,"context_lines":[{"line_number":1056,"context_line":"                   min\u003d1,"},{"line_number":1057,"context_line":"                   help\u003d\u0027Number of days for which a user can be inactive \u0027"},{"line_number":1058,"context_line":"                        \u0027before the account becomes disabled. Setting the \u0027"},{"line_number":1059,"context_line":"                        \u0027value to None disables this feature. Note: this \u0027"},{"line_number":1060,"context_line":"                        \u0027feature is only supported via the SQL backend \u0027"},{"line_number":1061,"context_line":"                        \u0027driver for identity.\u0027),"},{"line_number":1062,"context_line":"        cfg.IntOpt(\u0027lockout_failure_attempts\u0027,"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_51dfce0c","line":1059,"updated":"2016-06-23 16:23:30.000000000","message":"you can\u0027t actually set this to \"None\" in keystone conf, as that would be a string value of \"None\", not a null value (the actual default). Instead, simply don\u0027t set a value to disable the feature.","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"ed2a83c0405e3c44a7cdb59497ccf7f108be3fd5","unresolved":false,"context_lines":[{"line_number":1056,"context_line":"                   min\u003d1,"},{"line_number":1057,"context_line":"                   help\u003d\u0027Number of days for which a user can be inactive \u0027"},{"line_number":1058,"context_line":"                        \u0027before the account becomes disabled. Setting the \u0027"},{"line_number":1059,"context_line":"                        \u0027value to None disables this feature. Note: this \u0027"},{"line_number":1060,"context_line":"                        \u0027feature is only supported via the SQL backend \u0027"},{"line_number":1061,"context_line":"                        \u0027driver for identity.\u0027),"},{"line_number":1062,"context_line":"        cfg.IntOpt(\u0027lockout_failure_attempts\u0027,"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_b95d3849","line":1059,"in_reply_to":"3aaa91ec_51dfce0c","updated":"2016-06-23 17:06:17.000000000","message":"Done","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"}],"keystone/common/sql/migrate_repo/versions/105_add_last_auth_date_column.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0e90c3849065fdff4afe001a16323f8e87029a97","unresolved":false,"context_lines":[{"line_number":19,"context_line":""},{"line_number":20,"context_line":"    last_auth_at_column \u003d sql.Column(\u0027last_auth_at\u0027, sql.DateTime(),"},{"line_number":21,"context_line":"                                     nullable\u003dTrue)"},{"line_number":22,"context_line":"    local_user_table \u003d sql.Table(\u0027local_user\u0027, meta, autoload\u003dTrue)"},{"line_number":23,"context_line":"    local_user_table.create_column(last_auth_at_column)"}],"source_content_type":"text/x-python","patch_set":17,"id":"7aa08908_2c3f28b5","line":22,"updated":"2016-06-13 17:56:46.000000000","message":"Random question - this means that we won\u0027t be mirroring last_auth_at for shadowed users at all. Which should be fine because this is a PCI thing and if someone is already using an Identity Provider or LDAP somewhere, chances are they should just use that for PCI anyway (just thinking out loud).","commit_id":"9512681ba4333e03f780f4ac7fbe4657faf4ed39"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"7ff857ad7aa98bc5996116ea4f44d2804dab8ec8","unresolved":false,"context_lines":[{"line_number":19,"context_line":""},{"line_number":20,"context_line":"    last_auth_at_column \u003d sql.Column(\u0027last_auth_at\u0027, sql.DateTime(),"},{"line_number":21,"context_line":"                                     nullable\u003dTrue)"},{"line_number":22,"context_line":"    local_user_table \u003d sql.Table(\u0027local_user\u0027, meta, autoload\u003dTrue)"},{"line_number":23,"context_line":"    local_user_table.create_column(last_auth_at_column)"}],"source_content_type":"text/x-python","patch_set":17,"id":"7aa08908_0cfb4c7c","line":22,"in_reply_to":"7aa08908_2c3f28b5","updated":"2016-06-13 18:09:02.000000000","message":"Yeah, agree.","commit_id":"9512681ba4333e03f780f4ac7fbe4657faf4ed39"}],"keystone/common/sql/migrate_repo/versions/105_add_user_date_columns.py":[{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c563544049b48825d021288e40a0d21ebabd5eb6","unresolved":false,"context_lines":[{"line_number":20,"context_line":"    meta.bind \u003d migrate_engine"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"    created_at \u003d sql.Column(\u0027created_at\u0027, sql.DateTime(), nullable\u003dTrue)"},{"line_number":23,"context_line":"    last_auth_at \u003d sql.Column(\u0027last_auth_at\u0027, sql.DateTime(), nullable\u003dTrue)"},{"line_number":24,"context_line":"    user_table \u003d sql.Table(\u0027user\u0027, meta, autoload\u003dTrue)"},{"line_number":25,"context_line":"    user_table.create_column(created_at)"},{"line_number":26,"context_line":"    user_table.create_column(last_auth_at)"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_af2cf801","line":23,"updated":"2016-06-16 16:41:36.000000000","message":"This should just be a date, not a datetime. We don\u0027t need or want the extra precision, because we don\u0027t want to update this more than once a day. http://docs.sqlalchemy.org/en/latest/core/type_basics.html#sqlalchemy.types.Date","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a4b5680b4fce23e0073cc9ccbc9cb1007e6c85bf","unresolved":false,"context_lines":[{"line_number":20,"context_line":"    meta.bind \u003d migrate_engine"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"    created_at \u003d sql.Column(\u0027created_at\u0027, sql.DateTime(), nullable\u003dTrue)"},{"line_number":23,"context_line":"    last_auth_at \u003d sql.Column(\u0027last_auth_at\u0027, sql.DateTime(), nullable\u003dTrue)"},{"line_number":24,"context_line":"    user_table \u003d sql.Table(\u0027user\u0027, meta, autoload\u003dTrue)"},{"line_number":25,"context_line":"    user_table.create_column(created_at)"},{"line_number":26,"context_line":"    user_table.create_column(last_auth_at)"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_2af3d6e8","line":23,"in_reply_to":"7aa08908_af2cf801","updated":"2016-06-16 16:48:45.000000000","message":"++ that would still allow this to be sanely replicate-able.","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"82299a416724ff9d30919fed5f184f5eaf9d224f","unresolved":false,"context_lines":[{"line_number":20,"context_line":"    meta.bind \u003d migrate_engine"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"    created_at \u003d sql.Column(\u0027created_at\u0027, sql.DateTime(), nullable\u003dTrue)"},{"line_number":23,"context_line":"    last_auth_at \u003d sql.Column(\u0027last_auth_at\u0027, sql.DateTime(), nullable\u003dTrue)"},{"line_number":24,"context_line":"    user_table \u003d sql.Table(\u0027user\u0027, meta, autoload\u003dTrue)"},{"line_number":25,"context_line":"    user_table.create_column(created_at)"},{"line_number":26,"context_line":"    user_table.create_column(last_auth_at)"}],"source_content_type":"text/x-python","patch_set":29,"id":"3aaa91ec_0ec8c421","line":23,"in_reply_to":"7aa08908_af2cf801","updated":"2016-06-22 17:03:17.000000000","message":"Done","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"}],"keystone/conf/security_compliance.py":[{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c883f59c64f9b954a2d929ff55cb4f1aa3bf18fe","unresolved":false,"context_lines":[{"line_number":23,"context_line":"Number of days for which a user can be inactive before the account becomes"},{"line_number":24,"context_line":"disabled. This feature is disabled by default. Note: this feature is only"},{"line_number":25,"context_line":"supported via the SQL backend driver for identity. In addition, whether or"},{"line_number":26,"context_line":"not a user is disabled will be handled by the API and may not match the"},{"line_number":27,"context_line":"user table enabled column in the database."},{"line_number":28,"context_line":"\"\"\"))"},{"line_number":29,"context_line":""}],"source_content_type":"text/x-python","patch_set":61,"id":"3aaa91ec_ef58528c","line":26,"updated":"2016-06-30 16:41:05.000000000","message":"\"will be handled by the API\" \n\nThe interface doesn\u0027t do anything by itself :) I\u0027d phrase this as \"keystone will enforce this behavior regardless of the value of the `enabled` column in the database.\" So, it\u0027s an implementation detail of keystone, not a \"behavior of the API.\"","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8762c0c182dfa57531f07f25d31ffc793cfc9fd3","unresolved":false,"context_lines":[{"line_number":23,"context_line":"Number of days for which a user can be inactive before the account becomes"},{"line_number":24,"context_line":"disabled. This feature is disabled by default. Note: this feature is only"},{"line_number":25,"context_line":"supported via the SQL backend driver for identity. In addition, whether or"},{"line_number":26,"context_line":"not a user is disabled will be handled by the API and may not match the"},{"line_number":27,"context_line":"user table enabled column in the database."},{"line_number":28,"context_line":"\"\"\"))"},{"line_number":29,"context_line":""}],"source_content_type":"text/x-python","patch_set":61,"id":"1aa78d24_bd7b34bd","line":26,"in_reply_to":"3aaa91ec_ef58528c","updated":"2016-07-12 19:29:44.000000000","message":"Done","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"}],"keystone/exception.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"1589ab6b2abca5bf8891fb4661137bdb3129a95d","unresolved":false,"context_lines":[{"line_number":213,"context_line":""},{"line_number":214,"context_line":""},{"line_number":215,"context_line":"class UserDisabled(Unauthorized):"},{"line_number":216,"context_line":"    message_format \u003d _(\"The user account is disabled: %(user_id)s\")"},{"line_number":217,"context_line":""},{"line_number":218,"context_line":""},{"line_number":219,"context_line":"class AuthMethodNotSupported(AuthPluginException):"}],"source_content_type":"text/x-python","patch_set":15,"id":"7aa08908_36c77c99","line":216,"updated":"2016-06-13 16:03:47.000000000","message":"nit: this might read different depending on which perspective you use, because the message is going to output like: \n\n    The user account is disabled: f66b45207f7c4d00abc3a03c2e795111\n\nThe way the message reads makes it seem like f66b45207f7c4d00abc3a03c2e795111 is the ID of the account, not the user.\n\nMaybe we could do something like: \n\n    The account for user f66b45207f7c4d00abc3a03c2e795111 has been disabled.","commit_id":"22921f15ba52fe0b88c206b9adec1a04491aa516"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"823ccda23af9f87cf5912a65f62cd94b9bef05e1","unresolved":false,"context_lines":[{"line_number":213,"context_line":""},{"line_number":214,"context_line":""},{"line_number":215,"context_line":"class UserDisabled(Unauthorized):"},{"line_number":216,"context_line":"    message_format \u003d _(\"The user account is disabled: %(user_id)s\")"},{"line_number":217,"context_line":""},{"line_number":218,"context_line":""},{"line_number":219,"context_line":"class AuthMethodNotSupported(AuthPluginException):"}],"source_content_type":"text/x-python","patch_set":15,"id":"7aa08908_3106661c","line":216,"in_reply_to":"7aa08908_36c77c99","updated":"2016-06-13 16:15:42.000000000","message":"Done","commit_id":"22921f15ba52fe0b88c206b9adec1a04491aa516"}],"keystone/identity/backends/sql.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"1589ab6b2abca5bf8891fb4661137bdb3129a95d","unresolved":false,"context_lines":[{"line_number":268,"context_line":"        with sql.session_for_write() as session:"},{"line_number":269,"context_line":"            user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":270,"context_line":"            user_ref.enabled \u003d False"},{"line_number":271,"context_line":"            return"},{"line_number":272,"context_line":""},{"line_number":273,"context_line":"    def _set_last_auth(self, user_id):"},{"line_number":274,"context_line":"        with sql.session_for_write() as session:"}],"source_content_type":"text/x-python","patch_set":15,"id":"7aa08908_b6eeccea","line":271,"updated":"2016-06-13 16:03:47.000000000","message":"nit: are the returns needed since the method has no more statements? The return would essentially be the default behavior in this case.","commit_id":"22921f15ba52fe0b88c206b9adec1a04491aa516"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"823ccda23af9f87cf5912a65f62cd94b9bef05e1","unresolved":false,"context_lines":[{"line_number":268,"context_line":"        with sql.session_for_write() as session:"},{"line_number":269,"context_line":"            user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":270,"context_line":"            user_ref.enabled \u003d False"},{"line_number":271,"context_line":"            return"},{"line_number":272,"context_line":""},{"line_number":273,"context_line":"    def _set_last_auth(self, user_id):"},{"line_number":274,"context_line":"        with sql.session_for_write() as session:"}],"source_content_type":"text/x-python","patch_set":15,"id":"7aa08908_b148965d","line":271,"in_reply_to":"7aa08908_b6eeccea","updated":"2016-06-13 16:15:42.000000000","message":"Done","commit_id":"22921f15ba52fe0b88c206b9adec1a04491aa516"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"1589ab6b2abca5bf8891fb4661137bdb3129a95d","unresolved":false,"context_lines":[{"line_number":274,"context_line":"        with sql.session_for_write() as session:"},{"line_number":275,"context_line":"            user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":276,"context_line":"            user_ref.local_user.last_auth_at \u003d datetime.datetime.now()"},{"line_number":277,"context_line":"            return"}],"source_content_type":"text/x-python","patch_set":15,"id":"7aa08908_16b6c0f8","line":277,"updated":"2016-06-13 16:03:47.000000000","message":"nit: same comment here.","commit_id":"22921f15ba52fe0b88c206b9adec1a04491aa516"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"823ccda23af9f87cf5912a65f62cd94b9bef05e1","unresolved":false,"context_lines":[{"line_number":274,"context_line":"        with sql.session_for_write() as session:"},{"line_number":275,"context_line":"            user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":276,"context_line":"            user_ref.local_user.last_auth_at \u003d datetime.datetime.now()"},{"line_number":277,"context_line":"            return"}],"source_content_type":"text/x-python","patch_set":15,"id":"7aa08908_f174de9c","line":277,"in_reply_to":"7aa08908_16b6c0f8","updated":"2016-06-13 16:15:42.000000000","message":"Done","commit_id":"22921f15ba52fe0b88c206b9adec1a04491aa516"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"692bb10ca1c139cd74bf89bbbba55e362f00119c","unresolved":false,"context_lines":[{"line_number":235,"context_line":"            session.delete(ref)"},{"line_number":236,"context_line":""},{"line_number":237,"context_line":""},{"line_number":238,"context_line":"class Authenticator(object):"},{"line_number":239,"context_line":"    def authenticate(self, user_id, password):"},{"line_number":240,"context_line":"        user \u003d self._get_user(user_id)"},{"line_number":241,"context_line":"        if not user or not utils.check_password(password, user.password):"}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_ff402461","line":238,"updated":"2016-06-13 18:32:21.000000000","message":"I\u0027m not a fan of this being a separate class here as I don\u0027t think it makes sense, but I won\u0027t block because of that.","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"161c451d2c9b114dd2829eac4923ab7c00a3c5a1","unresolved":false,"context_lines":[{"line_number":242,"context_line":"            raise AssertionError(_(\u0027Invalid user / password\u0027))"},{"line_number":243,"context_line":"        elif self._is_user_disabled(user):"},{"line_number":244,"context_line":"            raise exception.UserDisabled(user_id\u003duser_id)"},{"line_number":245,"context_line":"        self._set_last_auth(user_id)"},{"line_number":246,"context_line":"        return base.filter_user(user.to_dict())"},{"line_number":247,"context_line":""},{"line_number":248,"context_line":"    def _get_user(self, user_id):"}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_1fd9b0f3","line":245,"updated":"2016-06-13 18:26:51.000000000","message":"So, this is essentially a lazy migration because we are allowing last_auth_at to be nullable in the migration but it should never be null once someone authenticates. That will always be the case, right? Do we ever have the need to change last_auth_at to not be nullable in a release or two?","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"1c57458ccf79ba074b97ffcc9c007a1cf4f462af","unresolved":false,"context_lines":[{"line_number":242,"context_line":"            raise AssertionError(_(\u0027Invalid user / password\u0027))"},{"line_number":243,"context_line":"        elif self._is_user_disabled(user):"},{"line_number":244,"context_line":"            raise exception.UserDisabled(user_id\u003duser_id)"},{"line_number":245,"context_line":"        self._set_last_auth(user_id)"},{"line_number":246,"context_line":"        return base.filter_user(user.to_dict())"},{"line_number":247,"context_line":""},{"line_number":248,"context_line":"    def _get_user(self, user_id):"}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_824b83b0","line":245,"in_reply_to":"7aa08908_1fd9b0f3","updated":"2016-06-13 18:47:08.000000000","message":"Correct, however I think when you re-enable a user, you would set last_auth_at back to null.  Otherwise, they would be automatically disabled again.","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":5707,"name":"Henry Nash","email":"henryn@linux.vnet.ibm.com","username":"henry-nash"},"change_message_id":"bfdc45089721b1382a29a63f011d8741422165a4","unresolved":false,"context_lines":[{"line_number":256,"context_line":"            last_auth_at \u003d user.local_user.last_auth_at"},{"line_number":257,"context_line":"            max_days_inactive \u003d ("},{"line_number":258,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive)"},{"line_number":259,"context_line":"            if last_auth_at and max_days_inactive \u003e 0:"},{"line_number":260,"context_line":"                now \u003d datetime.datetime.utcnow()"},{"line_number":261,"context_line":"                days_inactive \u003d (now - last_auth_at).days"},{"line_number":262,"context_line":"                if days_inactive \u003e\u003d max_days_inactive:"}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_2d041b13","line":259,"range":{"start_line":259,"start_character":15,"end_line":259,"end_character":27},"updated":"2016-06-14 12:57:12.000000000","message":"the fact that we allow None here...does that mean that if, after migration, a user doesn\u0027t log in for 1 year....they will still be able to log in the first time, and that will start the clock ticking for 90 days? Is there a reason we don\u0027t set last_auth_at to now() in the migration?","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"8a5ec6ce807391fa131c865813871e8111b4ad3b","unresolved":false,"context_lines":[{"line_number":256,"context_line":"            last_auth_at \u003d user.local_user.last_auth_at"},{"line_number":257,"context_line":"            max_days_inactive \u003d ("},{"line_number":258,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive)"},{"line_number":259,"context_line":"            if last_auth_at and max_days_inactive \u003e 0:"},{"line_number":260,"context_line":"                now \u003d datetime.datetime.utcnow()"},{"line_number":261,"context_line":"                days_inactive \u003d (now - last_auth_at).days"},{"line_number":262,"context_line":"                if days_inactive \u003e\u003d max_days_inactive:"}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_acc8bba9","line":259,"range":{"start_line":259,"start_character":15,"end_line":259,"end_character":27},"in_reply_to":"7aa08908_2d041b13","updated":"2016-06-15 18:46:27.000000000","message":"It\u0027s a good point. I didn\u0027t want to give last_auth_at a default value, because I didn\u0027t want to assume that the user actually had authenticated previously. Perhaps what I could do is if last_auth_at is None, use the date when the user was created. I\u0027ll give this some thought. Let me know if you have any ideas.","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"692bb10ca1c139cd74bf89bbbba55e362f00119c","unresolved":false,"context_lines":[{"line_number":260,"context_line":"                now \u003d datetime.datetime.utcnow()"},{"line_number":261,"context_line":"                days_inactive \u003d (now - last_auth_at).days"},{"line_number":262,"context_line":"                if days_inactive \u003e\u003d max_days_inactive:"},{"line_number":263,"context_line":"                    self._disable_user(user[\u0027id\u0027])"},{"line_number":264,"context_line":"                    return True"},{"line_number":265,"context_line":"            return False"},{"line_number":266,"context_line":""}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_3f262cce","line":263,"updated":"2016-06-13 18:32:21.000000000","message":"So as a cloud operator I need to unset this boolean and fake the last_auth_at to re-enable the user?","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"c10a35afcc9872d28bf09c0f0f588d5b588f3641","unresolved":false,"context_lines":[{"line_number":260,"context_line":"                now \u003d datetime.datetime.utcnow()"},{"line_number":261,"context_line":"                days_inactive \u003d (now - last_auth_at).days"},{"line_number":262,"context_line":"                if days_inactive \u003e\u003d max_days_inactive:"},{"line_number":263,"context_line":"                    self._disable_user(user[\u0027id\u0027])"},{"line_number":264,"context_line":"                    return True"},{"line_number":265,"context_line":"            return False"},{"line_number":266,"context_line":""}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_821703ed","line":263,"in_reply_to":"7aa08908_3f262cce","updated":"2016-06-13 18:37:46.000000000","message":"That sounds like something we can add to keystone-manage.","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"1c6f1e720c07557b71d61af686db857617b4f92b","unresolved":false,"context_lines":[{"line_number":260,"context_line":"                now \u003d datetime.datetime.utcnow()"},{"line_number":261,"context_line":"                days_inactive \u003d (now - last_auth_at).days"},{"line_number":262,"context_line":"                if days_inactive \u003e\u003d max_days_inactive:"},{"line_number":263,"context_line":"                    self._disable_user(user[\u0027id\u0027])"},{"line_number":264,"context_line":"                    return True"},{"line_number":265,"context_line":"            return False"},{"line_number":266,"context_line":""}],"source_content_type":"text/x-python","patch_set":18,"id":"3aaa91ec_cef35c9d","line":263,"in_reply_to":"7aa08908_3f262cce","updated":"2016-06-22 17:01:33.000000000","message":"This has been fixed so that when admin re-enables a user, the last_active_at gets reset.","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":5707,"name":"Henry Nash","email":"henryn@linux.vnet.ibm.com","username":"henry-nash"},"change_message_id":"bfdc45089721b1382a29a63f011d8741422165a4","unresolved":false,"context_lines":[{"line_number":269,"context_line":"            user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":270,"context_line":"            user_ref.enabled \u003d False"},{"line_number":271,"context_line":""},{"line_number":272,"context_line":"    def _set_last_auth(self, user_id):"},{"line_number":273,"context_line":"        with sql.session_for_write() as session:"},{"line_number":274,"context_line":"            user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":275,"context_line":"            user_ref.local_user.last_auth_at \u003d datetime.datetime.now()"}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_6d362352","line":275,"range":{"start_line":272,"start_character":0,"end_line":275,"end_character":70},"updated":"2016-06-14 12:57:12.000000000","message":"Wouldn\u0027t we want the maintaining of last_auth to be optional, controlled by a config switch? I.e. if CONF.security_compliance.disable_user_account_days_inactive was zero? Otherwise, we are now further forcing user table replication of write updates on authentication...which could be very bad in multi-data centre configurations.","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"3e347a13e67310dc91c71a8f6ce1e1a15ce8219f","unresolved":false,"context_lines":[{"line_number":269,"context_line":"            user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":270,"context_line":"            user_ref.enabled \u003d False"},{"line_number":271,"context_line":""},{"line_number":272,"context_line":"    def _set_last_auth(self, user_id):"},{"line_number":273,"context_line":"        with sql.session_for_write() as session:"},{"line_number":274,"context_line":"            user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":275,"context_line":"            user_ref.local_user.last_auth_at \u003d datetime.datetime.now()"}],"source_content_type":"text/x-python","patch_set":18,"id":"3aaa91ec_6e9fc850","line":275,"range":{"start_line":272,"start_character":0,"end_line":275,"end_character":70},"in_reply_to":"7aa08908_6d362352","updated":"2016-06-22 16:59:42.000000000","message":"Done","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"3e347a13e67310dc91c71a8f6ce1e1a15ce8219f","unresolved":false,"context_lines":[{"line_number":269,"context_line":"            user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":270,"context_line":"            user_ref.enabled \u003d False"},{"line_number":271,"context_line":""},{"line_number":272,"context_line":"    def _set_last_auth(self, user_id):"},{"line_number":273,"context_line":"        with sql.session_for_write() as session:"},{"line_number":274,"context_line":"            user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":275,"context_line":"            user_ref.local_user.last_auth_at \u003d datetime.datetime.now()"}],"source_content_type":"text/x-python","patch_set":18,"id":"3aaa91ec_ae725047","line":275,"range":{"start_line":272,"start_character":0,"end_line":275,"end_character":70},"in_reply_to":"7aa08908_6d362352","updated":"2016-06-22 16:59:42.000000000","message":"Done","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c563544049b48825d021288e40a0d21ebabd5eb6","unresolved":false,"context_lines":[{"line_number":240,"context_line":"    def authenticate(self, user_id, password):"},{"line_number":241,"context_line":"        user \u003d self._get_user(user_id)"},{"line_number":242,"context_line":"        if not user or not utils.check_password(password, user.password):"},{"line_number":243,"context_line":"            raise AssertionError(_(\u0027Invalid user / password\u0027))"},{"line_number":244,"context_line":"        elif self._is_user_disabled(user):"},{"line_number":245,"context_line":"            raise exception.UserDisabled(user_id\u003duser_id)"},{"line_number":246,"context_line":"        return base.filter_user(user.to_dict())"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_8fa93c27","line":243,"updated":"2016-06-16 16:41:36.000000000","message":"Is something explicitly expecting an AssertionError from this method\u0027s caller? I know there used to be. If that code still exists, I don\u0027t know how it\u0027ll handle a specific 401 exception being raised instead.","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"bbd337fec4134573a459336f83ede10751b82f83","unresolved":false,"context_lines":[{"line_number":240,"context_line":"    def authenticate(self, user_id, password):"},{"line_number":241,"context_line":"        user \u003d self._get_user(user_id)"},{"line_number":242,"context_line":"        if not user or not utils.check_password(password, user.password):"},{"line_number":243,"context_line":"            raise AssertionError(_(\u0027Invalid user / password\u0027))"},{"line_number":244,"context_line":"        elif self._is_user_disabled(user):"},{"line_number":245,"context_line":"            raise exception.UserDisabled(user_id\u003duser_id)"},{"line_number":246,"context_line":"        return base.filter_user(user.to_dict())"}],"source_content_type":"text/x-python","patch_set":29,"id":"3aaa91ec_e1f1096c","line":243,"in_reply_to":"7aa08908_8fa93c27","updated":"2016-06-22 17:24:54.000000000","message":"The AssertionError gets caught here, which raises an Unauthorized exception:\nhttps://github.com/openstack/keystone/blob/master/keystone/auth/plugins/password.py#L40","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c563544049b48825d021288e40a0d21ebabd5eb6","unresolved":false,"context_lines":[{"line_number":249,"context_line":"        with sql.session_for_read() as session:"},{"line_number":250,"context_line":"            return session.query(model.User).get(user_id)"},{"line_number":251,"context_line":""},{"line_number":252,"context_line":"    def _is_user_disabled(self, user):"},{"line_number":253,"context_line":"        if not user.enabled:"},{"line_number":254,"context_line":"            return True"},{"line_number":255,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_af199889","line":252,"updated":"2016-06-16 16:41:36.000000000","message":"It\u0027d require a lot of refactoring and I\u0027m not sure it\u0027s possible to stomp on the model\u0027s existing property, but it\u0027d be easier for me to read this as a hybrid property on the user object, and just have it read as the normal \"user.enabled\"","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"d73bb5f7e94a1d0eaf50ed86a1a0cc6c950ba03c","unresolved":false,"context_lines":[{"line_number":249,"context_line":"        with sql.session_for_read() as session:"},{"line_number":250,"context_line":"            return session.query(model.User).get(user_id)"},{"line_number":251,"context_line":""},{"line_number":252,"context_line":"    def _is_user_disabled(self, user):"},{"line_number":253,"context_line":"        if not user.enabled:"},{"line_number":254,"context_line":"            return True"},{"line_number":255,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":29,"id":"3aaa91ec_b30c8195","line":252,"in_reply_to":"7aa08908_af199889","updated":"2016-06-22 16:56:34.000000000","message":"Done","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"bd7c852c05691f868c74c74fba6057d7ce5f047f","unresolved":false,"context_lines":[{"line_number":252,"context_line":"    def _is_user_disabled(self, user):"},{"line_number":253,"context_line":"        if not user.enabled:"},{"line_number":254,"context_line":"            return True"},{"line_number":255,"context_line":"        else:"},{"line_number":256,"context_line":"            last_auth_at \u003d (user.last_auth_at if"},{"line_number":257,"context_line":"                            user.last_auth_at else user.created_at)"},{"line_number":258,"context_line":"            max_days_inactive \u003d ("}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_2a1ab618","line":255,"updated":"2016-06-16 17:03:44.000000000","message":"nit: Leave the above if block alone, and treat it as a short circuit ... remove the else and un-indent the rest of the method :) Just a good habit to reduce  make the 79 char limit easier to maintain with a lower perceived mccabe complexity for readers.","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"d73bb5f7e94a1d0eaf50ed86a1a0cc6c950ba03c","unresolved":false,"context_lines":[{"line_number":252,"context_line":"    def _is_user_disabled(self, user):"},{"line_number":253,"context_line":"        if not user.enabled:"},{"line_number":254,"context_line":"            return True"},{"line_number":255,"context_line":"        else:"},{"line_number":256,"context_line":"            last_auth_at \u003d (user.last_auth_at if"},{"line_number":257,"context_line":"                            user.last_auth_at else user.created_at)"},{"line_number":258,"context_line":"            max_days_inactive \u003d ("}],"source_content_type":"text/x-python","patch_set":29,"id":"3aaa91ec_f3b0c98d","line":255,"in_reply_to":"7aa08908_2a1ab618","updated":"2016-06-22 16:56:34.000000000","message":"Done","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":5707,"name":"Henry Nash","email":"henryn@linux.vnet.ibm.com","username":"henry-nash"},"change_message_id":"d4c60b423e7730ff8359ce23bd8df832eb281768","unresolved":false,"context_lines":[{"line_number":254,"context_line":"            return True"},{"line_number":255,"context_line":"        else:"},{"line_number":256,"context_line":"            last_auth_at \u003d (user.last_auth_at if"},{"line_number":257,"context_line":"                            user.last_auth_at else user.created_at)"},{"line_number":258,"context_line":"            max_days_inactive \u003d ("},{"line_number":259,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive)"},{"line_number":260,"context_line":"            if last_auth_at and max_days_inactive \u003e 0:"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_bb6bb384","line":257,"range":{"start_line":257,"start_character":28,"end_line":257,"end_character":67},"updated":"2016-06-16 11:01:18.000000000","message":"So I know you have done this to try and keep last_auth always \"correct\" (i.e. it\u0027s None if they haven\u0027t authenticated yet after upgrading to Newton). However, this new solution swaps a \"temporary incorrectness\", for a larger percentage of items bering \"permanently incorrect\" - i.e. all those users created prior to Newton. I\u0027m not quite sure that\u0027s a good trade-off.","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"1ce2ee7e951809c29af992754d7cab88d2543da3","unresolved":false,"context_lines":[{"line_number":254,"context_line":"            return True"},{"line_number":255,"context_line":"        else:"},{"line_number":256,"context_line":"            last_auth_at \u003d (user.last_auth_at if"},{"line_number":257,"context_line":"                            user.last_auth_at else user.created_at)"},{"line_number":258,"context_line":"            max_days_inactive \u003d ("},{"line_number":259,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive)"},{"line_number":260,"context_line":"            if last_auth_at and max_days_inactive \u003e 0:"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_f710a45b","line":257,"range":{"start_line":257,"start_character":28,"end_line":257,"end_character":67},"in_reply_to":"7aa08908_bb6bb384","updated":"2016-06-16 13:52:19.000000000","message":"Not following. This issue was that if the user never authenticated, then the clock wouldn\u0027t start ticking until then. Now if that is the case, the clock would start at when the user was created. Seems reasonable to me... What am I missing?","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":5707,"name":"Henry Nash","email":"henryn@linux.vnet.ibm.com","username":"henry-nash"},"change_message_id":"bbeef229c8bb456552baa0a69c3f1c0b4e77b10f","unresolved":false,"context_lines":[{"line_number":254,"context_line":"            return True"},{"line_number":255,"context_line":"        else:"},{"line_number":256,"context_line":"            last_auth_at \u003d (user.last_auth_at if"},{"line_number":257,"context_line":"                            user.last_auth_at else user.created_at)"},{"line_number":258,"context_line":"            max_days_inactive \u003d ("},{"line_number":259,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive)"},{"line_number":260,"context_line":"            if last_auth_at and max_days_inactive \u003e 0:"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_63751231","line":257,"range":{"start_line":257,"start_character":28,"end_line":257,"end_character":67},"in_reply_to":"7aa08908_f710a45b","updated":"2016-06-16 14:53:39.000000000","message":"so I may be misreading this, but seems like there are two solutions to the original problem:\n\n1) you only have last_auth_at, and this is set to now() for all existing rows at migration time (this was what I was trying to suggest)\n2) you have last_auth_at and create_at, with last_auth_at set to None and created_at set to now() at migration time.\n\nI think you said you didn\u0027t like 1) because it gave an \"incorrect\" last_auth_at (until the user really did authenticate).  However, with 2) for all users that exist prior to migration, created_at will be set to now() and never be updated....i.e. per permanently \"wrong\" (since for sure, none of those users where actually created at now()). With 1) the, period of \"incorrectness\" only lasts as along as it takes for the user to next authenticate or of the user.\n\nBoth \"work\" in terms of the PCI functionally, just seems odd to have a scheme that creates permanently incorrect data. An alternative would be to not call it \"created_at\"....you could call it \"migrated_at\"....?","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"bd7c852c05691f868c74c74fba6057d7ce5f047f","unresolved":false,"context_lines":[{"line_number":258,"context_line":"            max_days_inactive \u003d ("},{"line_number":259,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive)"},{"line_number":260,"context_line":"            if last_auth_at and max_days_inactive \u003e 0:"},{"line_number":261,"context_line":"                now \u003d datetime.datetime.utcnow()"},{"line_number":262,"context_line":"                days_inactive \u003d (now - last_auth_at).days"},{"line_number":263,"context_line":"                if days_inactive \u003e\u003d max_days_inactive:"},{"line_number":264,"context_line":"                    self._disable_user(user[\u0027id\u0027])"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_6a667ebc","line":261,"updated":"2016-06-16 17:03:44.000000000","message":"if last_auth_at is a date, then this could probably be a datetime.date() as well","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"d73bb5f7e94a1d0eaf50ed86a1a0cc6c950ba03c","unresolved":false,"context_lines":[{"line_number":258,"context_line":"            max_days_inactive \u003d ("},{"line_number":259,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive)"},{"line_number":260,"context_line":"            if last_auth_at and max_days_inactive \u003e 0:"},{"line_number":261,"context_line":"                now \u003d datetime.datetime.utcnow()"},{"line_number":262,"context_line":"                days_inactive \u003d (now - last_auth_at).days"},{"line_number":263,"context_line":"                if days_inactive \u003e\u003d max_days_inactive:"},{"line_number":264,"context_line":"                    self._disable_user(user[\u0027id\u0027])"}],"source_content_type":"text/x-python","patch_set":29,"id":"3aaa91ec_134755bc","line":261,"in_reply_to":"7aa08908_6a667ebc","updated":"2016-06-22 16:56:34.000000000","message":"Done","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"7cd215155d940f4e8c6957e7f95678fe9701e273","unresolved":false,"context_lines":[{"line_number":50,"context_line":"    # Identity interface"},{"line_number":51,"context_line":"    def authenticate(self, user_id, password):"},{"line_number":52,"context_line":"        with sql.session_for_read() as session:"},{"line_number":53,"context_line":"            user_ref \u003d None"},{"line_number":54,"context_line":"            try:"},{"line_number":55,"context_line":"                user_ref \u003d self._get_user(session, user_id)"},{"line_number":56,"context_line":"            except exception.UserNotFound:"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_f1a51a47","line":53,"updated":"2016-06-23 16:23:30.000000000","message":"This line isn\u0027t necessary.","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"ed2a83c0405e3c44a7cdb59497ccf7f108be3fd5","unresolved":false,"context_lines":[{"line_number":50,"context_line":"    # Identity interface"},{"line_number":51,"context_line":"    def authenticate(self, user_id, password):"},{"line_number":52,"context_line":"        with sql.session_for_read() as session:"},{"line_number":53,"context_line":"            user_ref \u003d None"},{"line_number":54,"context_line":"            try:"},{"line_number":55,"context_line":"                user_ref \u003d self._get_user(session, user_id)"},{"line_number":56,"context_line":"            except exception.UserNotFound:"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_9c1e6a5d","line":53,"in_reply_to":"3aaa91ec_f1a51a47","updated":"2016-06-23 17:06:17.000000000","message":"It was there previously, but took it out.","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"f342945df3fd7acc1e3d4cc8bb25e0ee67e2bef7","unresolved":false,"context_lines":[{"line_number":67,"context_line":"        user \u003d utils.hash_user_password(user)"},{"line_number":68,"context_line":"        with sql.session_for_write() as session:"},{"line_number":69,"context_line":"            user_ref \u003d model.User.from_dict(user)"},{"line_number":70,"context_line":"            user_ref.created_at \u003d datetime.datetime.utcnow()"},{"line_number":71,"context_line":"            session.add(user_ref)"},{"line_number":72,"context_line":"            return base.filter_user(user_ref.to_dict())"},{"line_number":73,"context_line":""}],"source_content_type":"text/x-python","patch_set":61,"id":"3aaa91ec_0f5cbe49","line":70,"updated":"2016-06-30 16:41:17.000000000","message":"It would be nice if we could be sqlalchemy to do this for us....","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"}],"keystone/identity/backends/sql_model.py":[{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"692bb10ca1c139cd74bf89bbbba55e362f00119c","unresolved":false,"context_lines":[{"line_number":144,"context_line":"                                 lazy\u003d\u0027subquery\u0027,"},{"line_number":145,"context_line":"                                 backref\u003d\u0027local_user\u0027,"},{"line_number":146,"context_line":"                                 order_by\u003d\u0027Password.created_at\u0027)"},{"line_number":147,"context_line":"    last_auth_at \u003d sql.Column(sql.DateTime, nullable\u003dTrue)"},{"line_number":148,"context_line":"    __table_args__ \u003d (sql.UniqueConstraint(\u0027domain_id\u0027, \u0027name\u0027), {})"},{"line_number":149,"context_line":""},{"line_number":150,"context_line":""}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_825f030e","line":147,"updated":"2016-06-13 18:32:21.000000000","message":"Can this actually be here? What if they have a LocalUser and a FederatedUser, but usually use the federated identity. The first time they auth with local credentials *after* the 90 days will disable their account even them they authed using federation yesterday.","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"8dbd223d0a7388f1d4d77e5c1dc4ee4438e8489c","unresolved":false,"context_lines":[{"line_number":144,"context_line":"                                 lazy\u003d\u0027subquery\u0027,"},{"line_number":145,"context_line":"                                 backref\u003d\u0027local_user\u0027,"},{"line_number":146,"context_line":"                                 order_by\u003d\u0027Password.created_at\u0027)"},{"line_number":147,"context_line":"    last_auth_at \u003d sql.Column(sql.DateTime, nullable\u003dTrue)"},{"line_number":148,"context_line":"    __table_args__ \u003d (sql.UniqueConstraint(\u0027domain_id\u0027, \u0027name\u0027), {})"},{"line_number":149,"context_line":""},{"line_number":150,"context_line":""}],"source_content_type":"text/x-python","patch_set":18,"id":"3aaa91ec_6ec6a84a","line":147,"in_reply_to":"7aa08908_825f030e","updated":"2016-06-22 17:00:05.000000000","message":"Done","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"02f01219f2a26527959c96b2aeccedd85e702d29","unresolved":false,"context_lines":[{"line_number":144,"context_line":"                                 lazy\u003d\u0027subquery\u0027,"},{"line_number":145,"context_line":"                                 backref\u003d\u0027local_user\u0027,"},{"line_number":146,"context_line":"                                 order_by\u003d\u0027Password.created_at\u0027)"},{"line_number":147,"context_line":"    last_auth_at \u003d sql.Column(sql.DateTime, nullable\u003dTrue)"},{"line_number":148,"context_line":"    __table_args__ \u003d (sql.UniqueConstraint(\u0027domain_id\u0027, \u0027name\u0027), {})"},{"line_number":149,"context_line":""},{"line_number":150,"context_line":""}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_02b15301","line":147,"in_reply_to":"7aa08908_825f030e","updated":"2016-06-13 18:39:09.000000000","message":"That could be fixed after we implement account linking, right? If two accounts are linked, give last_auth_at precedence to the shadowed user versus checking the local user?","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"6582136cc596dc677ed177ddc012b27f50d08ac9","unresolved":false,"context_lines":[{"line_number":47,"context_line":"                                      cascade\u003d\u0027all,delete-orphan\u0027,"},{"line_number":48,"context_line":"                                      backref\u003d\u0027user\u0027)"},{"line_number":49,"context_line":"    created_at \u003d sql.Column(sql.DateTime, nullable\u003dTrue)"},{"line_number":50,"context_line":"    last_active_at \u003d sql.Column(sqlalchemy.Date, nullable\u003dTrue)"},{"line_number":51,"context_line":""},{"line_number":52,"context_line":"    # name property"},{"line_number":53,"context_line":"    @hybrid_property"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_835a8d28","line":50,"updated":"2016-06-23 16:22:01.000000000","message":"Shouldn\u0027t we use type references from `keystone.common.sql` like we do for everything else? https://github.com/openstack/keystone/blob/master/keystone/common/sql/core.py#L48-L66\n\nIt looks like we would have to add sqlalchemy.Date to `keystone.common.sql.core`.\n\n    Date \u003d sql.Date","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"ed2a83c0405e3c44a7cdb59497ccf7f108be3fd5","unresolved":false,"context_lines":[{"line_number":47,"context_line":"                                      cascade\u003d\u0027all,delete-orphan\u0027,"},{"line_number":48,"context_line":"                                      backref\u003d\u0027user\u0027)"},{"line_number":49,"context_line":"    created_at \u003d sql.Column(sql.DateTime, nullable\u003dTrue)"},{"line_number":50,"context_line":"    last_active_at \u003d sql.Column(sqlalchemy.Date, nullable\u003dTrue)"},{"line_number":51,"context_line":""},{"line_number":52,"context_line":"    # name property"},{"line_number":53,"context_line":"    @hybrid_property"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_26576727","line":50,"in_reply_to":"3aaa91ec_835a8d28","updated":"2016-06-23 17:06:17.000000000","message":"Yeah, there isn\u0027t a sql.Date, so chose to use sqlalchemy instead of extending keystone sql.","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"7cd215155d940f4e8c6957e7f95678fe9701e273","unresolved":false,"context_lines":[{"line_number":137,"context_line":"    @enabled.setter"},{"line_number":138,"context_line":"    def enabled(self, value):"},{"line_number":139,"context_line":"        # if user was disabled and now enabled, reset last_active_at"},{"line_number":140,"context_line":"        if (self._enabled is False and value and"},{"line_number":141,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive):"},{"line_number":142,"context_line":"            self.last_active_at \u003d datetime.datetime.utcnow().date()"},{"line_number":143,"context_line":"        self._enabled \u003d value"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_03ba1d0a","line":140,"updated":"2016-06-23 16:23:30.000000000","message":"`not self._enabled` is more \"pythonic\" than `self._enabled is False`, unless you need to distinguish between False and None and 0, for example, which it doesn\u0027t look like is the case here.","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"ed2a83c0405e3c44a7cdb59497ccf7f108be3fd5","unresolved":false,"context_lines":[{"line_number":137,"context_line":"    @enabled.setter"},{"line_number":138,"context_line":"    def enabled(self, value):"},{"line_number":139,"context_line":"        # if user was disabled and now enabled, reset last_active_at"},{"line_number":140,"context_line":"        if (self._enabled is False and value and"},{"line_number":141,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive):"},{"line_number":142,"context_line":"            self.last_active_at \u003d datetime.datetime.utcnow().date()"},{"line_number":143,"context_line":"        self._enabled \u003d value"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_7c29fe36","line":140,"in_reply_to":"3aaa91ec_03ba1d0a","updated":"2016-06-23 17:06:17.000000000","message":"I want to exclude when _enabled is None, so specifically checking False.  I\u0027ll add a comment here.","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"7cd215155d940f4e8c6957e7f95678fe9701e273","unresolved":false,"context_lines":[{"line_number":240,"context_line":"                          primary_key\u003dTrue)"},{"line_number":241,"context_line":""},{"line_number":242,"context_line":""},{"line_number":243,"context_line":"class UserEnabledChecker(object):"},{"line_number":244,"context_line":"    def check(self, user):"},{"line_number":245,"context_line":"        if not user._enabled:"},{"line_number":246,"context_line":"            return False"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_a37a2975","line":243,"updated":"2016-06-23 16:23:30.000000000","message":"Why does this need to be a class? It never uses `self`.","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"7101650c3979df1fe5d3545c3dc098af8c497e90","unresolved":false,"context_lines":[{"line_number":240,"context_line":"                          primary_key\u003dTrue)"},{"line_number":241,"context_line":""},{"line_number":242,"context_line":""},{"line_number":243,"context_line":"class UserEnabledChecker(object):"},{"line_number":244,"context_line":"    def check(self, user):"},{"line_number":245,"context_line":"        if not user._enabled:"},{"line_number":246,"context_line":"            return False"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_d207b27f","line":243,"in_reply_to":"3aaa91ec_7cb73ef5","updated":"2016-06-27 17:01:12.000000000","message":"Like I said in earlier reviews. Classes should have state. This is simply a utility function.","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"ed2a83c0405e3c44a7cdb59497ccf7f108be3fd5","unresolved":false,"context_lines":[{"line_number":240,"context_line":"                          primary_key\u003dTrue)"},{"line_number":241,"context_line":""},{"line_number":242,"context_line":""},{"line_number":243,"context_line":"class UserEnabledChecker(object):"},{"line_number":244,"context_line":"    def check(self, user):"},{"line_number":245,"context_line":"        if not user._enabled:"},{"line_number":246,"context_line":"            return False"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_7cb73ef5","line":243,"in_reply_to":"3aaa91ec_a37a2975","updated":"2016-06-23 17:06:17.000000000","message":"I like having small, well-focused classes. It groups together common functionality and makes it more readable. It\u0027s probably the Java in me (Oops, did I say that) :)","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"07d85b1144b011b55fe5ce6972aa29c9b3fc303a","unresolved":false,"context_lines":[{"line_number":240,"context_line":"                          primary_key\u003dTrue)"},{"line_number":241,"context_line":""},{"line_number":242,"context_line":""},{"line_number":243,"context_line":"class UserEnabledChecker(object):"},{"line_number":244,"context_line":"    def check(self, user):"},{"line_number":245,"context_line":"        if not user._enabled:"},{"line_number":246,"context_line":"            return False"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_be190c7c","line":243,"in_reply_to":"3aaa91ec_d207b27f","updated":"2016-06-27 19:01:06.000000000","message":"This is gone now, checkout the latest patch.","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"4a00fc58e2f031745c6b31d4d6f9eeb2b11644f5","unresolved":false,"context_lines":[{"line_number":47,"context_line":"                                      cascade\u003d\u0027all,delete-orphan\u0027,"},{"line_number":48,"context_line":"                                      backref\u003d\u0027user\u0027)"},{"line_number":49,"context_line":"    created_at \u003d sql.Column(sql.DateTime, nullable\u003dTrue)"},{"line_number":50,"context_line":"    last_active_at \u003d sql.Column(sqlalchemy.Date, nullable\u003dTrue)"},{"line_number":51,"context_line":""},{"line_number":52,"context_line":"    # name property"},{"line_number":53,"context_line":"    @hybrid_property"}],"source_content_type":"text/x-python","patch_set":48,"id":"3aaa91ec_261f67b8","line":50,"updated":"2016-06-23 16:32:39.000000000","message":"Looks like I published my comments on patch set 46 after you got a few more iterations up. My comments will still apply.","commit_id":"265d142c5ad041ab761042b7dc52567a12fcecbd"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"eb92cd9576568c6929563bd4c0fe9552a35b9623","unresolved":false,"context_lines":[{"line_number":47,"context_line":"                                      cascade\u003d\u0027all,delete-orphan\u0027,"},{"line_number":48,"context_line":"                                      backref\u003d\u0027user\u0027)"},{"line_number":49,"context_line":"    created_at \u003d sql.Column(sql.DateTime, nullable\u003dTrue)"},{"line_number":50,"context_line":"    last_active_at \u003d sql.Column(sqlalchemy.Date, nullable\u003dTrue)"},{"line_number":51,"context_line":""},{"line_number":52,"context_line":"    # name property"},{"line_number":53,"context_line":"    @hybrid_property"}],"source_content_type":"text/x-python","patch_set":50,"id":"3aaa91ec_371ca8c0","line":50,"updated":"2016-06-23 19:51:48.000000000","message":"For the sake of consistency we should think about extending keystone.common.sql to include the sqlalchemy.Date type. That way all models get their types from the same place, at least until we refactor keystone.common.sql to not have types that don\u0027t need to be extended.","commit_id":"f113a51c968d307924e9ff375eabbedcbe654cc3"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"3434d53d8e318466514453303c105df9896e6976","unresolved":false,"context_lines":[{"line_number":47,"context_line":"                                      cascade\u003d\u0027all,delete-orphan\u0027,"},{"line_number":48,"context_line":"                                      backref\u003d\u0027user\u0027)"},{"line_number":49,"context_line":"    created_at \u003d sql.Column(sql.DateTime, nullable\u003dTrue)"},{"line_number":50,"context_line":"    last_active_at \u003d sql.Column(sqlalchemy.Date, nullable\u003dTrue)"},{"line_number":51,"context_line":""},{"line_number":52,"context_line":"    # name property"},{"line_number":53,"context_line":"    @hybrid_property"}],"source_content_type":"text/x-python","patch_set":50,"id":"3aaa91ec_bd6cf9d0","line":50,"in_reply_to":"3aaa91ec_371ca8c0","updated":"2016-06-23 20:25:40.000000000","message":"Done","commit_id":"f113a51c968d307924e9ff375eabbedcbe654cc3"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"c1097c69e88dc13c88bfe4e64646679f1e505001","unresolved":false,"context_lines":[{"line_number":137,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive)"},{"line_number":138,"context_line":"            last_active \u003d self.last_active_at"},{"line_number":139,"context_line":"            if not last_active and self.created_at:"},{"line_number":140,"context_line":"                last_active \u003d self.created_at.date()"},{"line_number":141,"context_line":"            if max_days and last_active:"},{"line_number":142,"context_line":"                now \u003d datetime.datetime.utcnow().date()"},{"line_number":143,"context_line":"                days_inactive \u003d (now - last_active).days"}],"source_content_type":"text/x-python","patch_set":60,"id":"3aaa91ec_19cf9ab4","line":140,"updated":"2016-06-27 19:32:12.000000000","message":"In the event users are disabled after X days, a user is created, but don\u0027t authenticate for X + 1 days, the first time that user authenticates will result in them being locked out, right? \n\nThat should be the intended behavior of that specific case, shouldn\u0027t it? \n\n(I\u0027m just thinking out loud here)","commit_id":"b2ef6b0257a902db43bd26dc9feb163412f0752c"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"87847f31f9594490d843cf4147d1ed8e2b76a7dc","unresolved":false,"context_lines":[{"line_number":137,"context_line":"                CONF.security_compliance.disable_user_account_days_inactive)"},{"line_number":138,"context_line":"            last_active \u003d self.last_active_at"},{"line_number":139,"context_line":"            if not last_active and self.created_at:"},{"line_number":140,"context_line":"                last_active \u003d self.created_at.date()"},{"line_number":141,"context_line":"            if max_days and last_active:"},{"line_number":142,"context_line":"                now \u003d datetime.datetime.utcnow().date()"},{"line_number":143,"context_line":"                days_inactive \u003d (now - last_active).days"}],"source_content_type":"text/x-python","patch_set":60,"id":"3aaa91ec_749fc350","line":140,"in_reply_to":"3aaa91ec_19cf9ab4","updated":"2016-06-27 19:53:17.000000000","message":"So if you set disable inactive users after 90 days and a user is created on day 1.  If the user tries to authenticate on day 91, they would be disabled. An admin would then need to re-enable their account. So yes, to me that is the expected behavior.","commit_id":"b2ef6b0257a902db43bd26dc9feb163412f0752c"},{"author":{"_account_id":6460,"name":"Brad Topol","email":"btopol@us.ibm.com","username":"btopol"},"change_message_id":"fbd7fee6f4c6b9ae6bd11e8a6531652e9ce192e8","unresolved":false,"context_lines":[{"line_number":153,"context_line":"        self._enabled \u003d value"},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"    @enabled.expression"},{"line_number":156,"context_line":"    def enabled(cls):"},{"line_number":157,"context_line":"        return User._enabled"},{"line_number":158,"context_line":""},{"line_number":159,"context_line":"    def to_dict(self, include_extra_dict\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":61,"id":"1aa78d24_fa2e908a","line":156,"updated":"2016-07-11 21:38:40.000000000","message":"Wow. First time I have seen hybrid properties used.  Pretty slick!","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"}],"keystone/identity/core.py":[{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c883f59c64f9b954a2d929ff55cb4f1aa3bf18fe","unresolved":false,"context_lines":[{"line_number":828,"context_line":"        ref \u003d self._set_domain_id_and_mapping("},{"line_number":829,"context_line":"            ref, domain_id, driver, mapping.EntityType.USER)"},{"line_number":830,"context_line":"        ref \u003d self._shadow_nonlocal_user(ref)"},{"line_number":831,"context_line":"        self.shadow_users_api.set_last_active_at(ref[\u0027id\u0027])"},{"line_number":832,"context_line":"        return ref"},{"line_number":833,"context_line":""},{"line_number":834,"context_line":"    def _assert_default_project_id_is_not_domain(self, default_project_id):"}],"source_content_type":"text/x-python","patch_set":61,"id":"3aaa91ec_4ab87411","line":831,"updated":"2016-06-30 16:41:05.000000000","message":"I really want this API to be touch_user()","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8762c0c182dfa57531f07f25d31ffc793cfc9fd3","unresolved":false,"context_lines":[{"line_number":828,"context_line":"        ref \u003d self._set_domain_id_and_mapping("},{"line_number":829,"context_line":"            ref, domain_id, driver, mapping.EntityType.USER)"},{"line_number":830,"context_line":"        ref \u003d self._shadow_nonlocal_user(ref)"},{"line_number":831,"context_line":"        self.shadow_users_api.set_last_active_at(ref[\u0027id\u0027])"},{"line_number":832,"context_line":"        return ref"},{"line_number":833,"context_line":""},{"line_number":834,"context_line":"    def _assert_default_project_id_is_not_domain(self, default_project_id):"}],"source_content_type":"text/x-python","patch_set":61,"id":"1aa78d24_5d0fb038","line":831,"in_reply_to":"3aaa91ec_4ab87411","updated":"2016-07-12 19:29:44.000000000","message":"Done","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"}],"keystone/identity/shadow_backends/base.py":[{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"bd7c852c05691f868c74c74fba6057d7ce5f047f","unresolved":false,"context_lines":[{"line_number":112,"context_line":"        return user_dict"},{"line_number":113,"context_line":""},{"line_number":114,"context_line":"    def set_last_auth_at(self, user_id):"},{"line_number":115,"context_line":"        return"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_4a7622d9","line":115,"updated":"2016-06-16 17:03:44.000000000","message":"nit: although both are no-ops, I would think pass would be more appropriate here","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"cc687c73bb8d78bf81b95e3612753028aeb6f30d","unresolved":false,"context_lines":[{"line_number":112,"context_line":"        return user_dict"},{"line_number":113,"context_line":""},{"line_number":114,"context_line":"    def set_last_auth_at(self, user_id):"},{"line_number":115,"context_line":"        return"}],"source_content_type":"text/x-python","patch_set":29,"id":"3aaa91ec_2eeca08e","line":115,"in_reply_to":"7aa08908_4a7622d9","updated":"2016-06-22 17:03:57.000000000","message":"Done","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"6582136cc596dc677ed177ddc012b27f50d08ac9","unresolved":false,"context_lines":[{"line_number":98,"context_line":"    def set_last_active_at(self, user_id):"},{"line_number":99,"context_line":"        \"\"\"Set the last active at date for the user."},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"        :param user_id: Identity of the user"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":"        \"\"\""},{"line_number":104,"context_line":"        raise exception.NotImplemented()"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_038cdd7d","line":101,"updated":"2016-06-23 16:22:01.000000000","message":"nit: s/Identity/Unique identifier/","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"ed2a83c0405e3c44a7cdb59497ccf7f108be3fd5","unresolved":false,"context_lines":[{"line_number":98,"context_line":"    def set_last_active_at(self, user_id):"},{"line_number":99,"context_line":"        \"\"\"Set the last active at date for the user."},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"        :param user_id: Identity of the user"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":"        \"\"\""},{"line_number":104,"context_line":"        raise exception.NotImplemented()"}],"source_content_type":"text/x-python","patch_set":46,"id":"3aaa91ec_9ce5cadd","line":101,"in_reply_to":"3aaa91ec_038cdd7d","updated":"2016-06-23 17:06:17.000000000","message":"Done","commit_id":"967ae646eb163071c9d26daea81ebe7d07bc5acd"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c883f59c64f9b954a2d929ff55cb4f1aa3bf18fe","unresolved":false,"context_lines":[{"line_number":95,"context_line":"        raise exception.NotImplemented()"},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"    @abc.abstractmethod"},{"line_number":98,"context_line":"    def set_last_active_at(self, user_id):"},{"line_number":99,"context_line":"        \"\"\"Set the last active at date for the user."},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"        :param user_id: Unique identifier of the user"}],"source_content_type":"text/x-python","patch_set":61,"id":"3aaa91ec_0accac87","line":98,"updated":"2016-06-30 16:41:05.000000000","message":".\n\n  def touch_user(self, user_id):\n    \"\"\"Reach out and touch someone.\"\"\"","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8762c0c182dfa57531f07f25d31ffc793cfc9fd3","unresolved":false,"context_lines":[{"line_number":95,"context_line":"        raise exception.NotImplemented()"},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"    @abc.abstractmethod"},{"line_number":98,"context_line":"    def set_last_active_at(self, user_id):"},{"line_number":99,"context_line":"        \"\"\"Set the last active at date for the user."},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"        :param user_id: Unique identifier of the user"}],"source_content_type":"text/x-python","patch_set":61,"id":"1aa78d24_501dd760","line":98,"in_reply_to":"3aaa91ec_0accac87","updated":"2016-07-12 19:29:44.000000000","message":"Done","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"26596c1590d7cde2289081f9b007a2722a0352ef","unresolved":false,"context_lines":[{"line_number":95,"context_line":"        raise exception.NotImplemented()"},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"    @abc.abstractmethod"},{"line_number":98,"context_line":"    def set_last_active_at(self, user_id):"},{"line_number":99,"context_line":"        \"\"\"Set the last active at date for the user."},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"        :param user_id: Unique identifier of the user"}],"source_content_type":"text/x-python","patch_set":61,"id":"3aaa91ec_efb20b48","line":98,"in_reply_to":"3aaa91ec_0accac87","updated":"2016-06-30 18:09:04.000000000","message":"Nice :)","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"}],"keystone/identity/shadow_backends/sql.py":[{"author":{"_account_id":5707,"name":"Henry Nash","email":"henryn@linux.vnet.ibm.com","username":"henry-nash"},"change_message_id":"d4c60b423e7730ff8359ce23bd8df832eb281768","unresolved":false,"context_lines":[{"line_number":68,"context_line":"            return user_ref"},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"    def set_last_auth_at(self, user_id):"},{"line_number":71,"context_line":"        if CONF.security_compliance.disable_user_account_days_inactive \u003e 0:"},{"line_number":72,"context_line":"            with sql.session_for_write() as session:"},{"line_number":73,"context_line":"                user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":74,"context_line":"                user_ref.last_auth_at \u003d datetime.datetime.now()"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_b502a2b2","line":71,"range":{"start_line":71,"start_character":11,"end_line":71,"end_character":70},"updated":"2016-06-16 11:01:18.000000000","message":"thanks for fixing that.","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"d73bb5f7e94a1d0eaf50ed86a1a0cc6c950ba03c","unresolved":false,"context_lines":[{"line_number":68,"context_line":"            return user_ref"},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"    def set_last_auth_at(self, user_id):"},{"line_number":71,"context_line":"        if CONF.security_compliance.disable_user_account_days_inactive \u003e 0:"},{"line_number":72,"context_line":"            with sql.session_for_write() as session:"},{"line_number":73,"context_line":"                user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":74,"context_line":"                user_ref.last_auth_at \u003d datetime.datetime.now()"}],"source_content_type":"text/x-python","patch_set":29,"id":"3aaa91ec_3804f0be","line":71,"in_reply_to":"7aa08908_4a0bc22b","updated":"2016-06-22 16:56:34.000000000","message":"Done","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"d73bb5f7e94a1d0eaf50ed86a1a0cc6c950ba03c","unresolved":false,"context_lines":[{"line_number":68,"context_line":"            return user_ref"},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"    def set_last_auth_at(self, user_id):"},{"line_number":71,"context_line":"        if CONF.security_compliance.disable_user_account_days_inactive \u003e 0:"},{"line_number":72,"context_line":"            with sql.session_for_write() as session:"},{"line_number":73,"context_line":"                user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":74,"context_line":"                user_ref.last_auth_at \u003d datetime.datetime.now()"}],"source_content_type":"text/x-python","patch_set":29,"id":"3aaa91ec_d8f68cd6","line":71,"in_reply_to":"7aa08908_8a75aa33","updated":"2016-06-22 16:56:34.000000000","message":"Done","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"bd7c852c05691f868c74c74fba6057d7ce5f047f","unresolved":false,"context_lines":[{"line_number":68,"context_line":"            return user_ref"},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"    def set_last_auth_at(self, user_id):"},{"line_number":71,"context_line":"        if CONF.security_compliance.disable_user_account_days_inactive \u003e 0:"},{"line_number":72,"context_line":"            with sql.session_for_write() as session:"},{"line_number":73,"context_line":"                user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":74,"context_line":"                user_ref.last_auth_at \u003d datetime.datetime.now()"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_4a0bc22b","line":71,"in_reply_to":"7aa08908_8a75aa33","updated":"2016-06-16 17:03:44.000000000","message":"Then you don\u0027t have to check for a specific value here, just something truthy.","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a4b5680b4fce23e0073cc9ccbc9cb1007e6c85bf","unresolved":false,"context_lines":[{"line_number":68,"context_line":"            return user_ref"},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"    def set_last_auth_at(self, user_id):"},{"line_number":71,"context_line":"        if CONF.security_compliance.disable_user_account_days_inactive \u003e 0:"},{"line_number":72,"context_line":"            with sql.session_for_write() as session:"},{"line_number":73,"context_line":"                user_ref \u003d session.query(model.User).get(user_id)"},{"line_number":74,"context_line":"                user_ref.last_auth_at \u003d datetime.datetime.now()"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_8a75aa33","line":71,"in_reply_to":"7aa08908_b502a2b2","updated":"2016-06-16 16:48:45.000000000","message":"We should probably change this if we respin disable_user_account_days_inactive to have a minimum value of 1.","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c883f59c64f9b954a2d929ff55cb4f1aa3bf18fe","unresolved":false,"context_lines":[{"line_number":14,"context_line":"import datetime"},{"line_number":15,"context_line":"import uuid"},{"line_number":16,"context_line":""},{"line_number":17,"context_line":"from oslo_config import cfg"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"from keystone.common import sql"},{"line_number":20,"context_line":"from keystone import exception"}],"source_content_type":"text/x-python","patch_set":61,"id":"3aaa91ec_aa2e3840","line":17,"updated":"2016-06-30 16:41:05.000000000","message":"import keystone.conf","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8762c0c182dfa57531f07f25d31ffc793cfc9fd3","unresolved":false,"context_lines":[{"line_number":14,"context_line":"import datetime"},{"line_number":15,"context_line":"import uuid"},{"line_number":16,"context_line":""},{"line_number":17,"context_line":"from oslo_config import cfg"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"from keystone.common import sql"},{"line_number":20,"context_line":"from keystone import exception"}],"source_content_type":"text/x-python","patch_set":61,"id":"1aa78d24_90429f3c","line":17,"in_reply_to":"3aaa91ec_aa2e3840","updated":"2016-07-12 19:29:44.000000000","message":"Done","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c883f59c64f9b954a2d929ff55cb4f1aa3bf18fe","unresolved":false,"context_lines":[{"line_number":23,"context_line":"from keystone.identity.shadow_backends import base"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"CONF \u003d cfg.CONF"},{"line_number":27,"context_line":""},{"line_number":28,"context_line":""},{"line_number":29,"context_line":"class ShadowUsers(base.ShadowUsersDriverV10):"}],"source_content_type":"text/x-python","patch_set":61,"id":"3aaa91ec_ca274461","line":26,"updated":"2016-06-30 16:41:05.000000000","message":"CONF \u003d keystone.conf.CONF","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8762c0c182dfa57531f07f25d31ffc793cfc9fd3","unresolved":false,"context_lines":[{"line_number":23,"context_line":"from keystone.identity.shadow_backends import base"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"CONF \u003d cfg.CONF"},{"line_number":27,"context_line":""},{"line_number":28,"context_line":""},{"line_number":29,"context_line":"class ShadowUsers(base.ShadowUsersDriverV10):"}],"source_content_type":"text/x-python","patch_set":61,"id":"1aa78d24_703f33b4","line":26,"in_reply_to":"3aaa91ec_ca274461","updated":"2016-07-12 19:29:44.000000000","message":"Done","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"}],"keystone/tests/unit/identity/backends/test_sql.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"1589ab6b2abca5bf8891fb4661137bdb3129a95d","unresolved":false,"context_lines":[{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class AuthenticatorTests(object):"},{"line_number":69,"context_line":"    def test_last_auth_date(self):"},{"line_number":70,"context_line":"        # create user"},{"line_number":71,"context_line":"        password \u003d uuid.uuid4().hex"},{"line_number":72,"context_line":"        user \u003d self._get_user_dict(password)"}],"source_content_type":"text/x-python","patch_set":15,"id":"7aa08908_d6943838","line":69,"updated":"2016-06-13 16:03:47.000000000","message":"Should `last_auth_at` also be updated when a user validates a token?","commit_id":"22921f15ba52fe0b88c206b9adec1a04491aa516"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"823ccda23af9f87cf5912a65f62cd94b9bef05e1","unresolved":false,"context_lines":[{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class AuthenticatorTests(object):"},{"line_number":69,"context_line":"    def test_last_auth_date(self):"},{"line_number":70,"context_line":"        # create user"},{"line_number":71,"context_line":"        password \u003d uuid.uuid4().hex"},{"line_number":72,"context_line":"        user \u003d self._get_user_dict(password)"}],"source_content_type":"text/x-python","patch_set":15,"id":"7aa08908_f15e9ee7","line":69,"in_reply_to":"7aa08908_d6943838","updated":"2016-06-13 16:15:42.000000000","message":"Would add some complexity; my preference would be to only base this on the last password authentication. However, let me know if you feel strongly otherwise.","commit_id":"22921f15ba52fe0b88c206b9adec1a04491aa516"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"46afc1e1a92529e2ee7c8657608667333a7a9dc2","unresolved":false,"context_lines":[{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class AuthenticatorTests(object):"},{"line_number":69,"context_line":"    def test_last_auth_date(self):"},{"line_number":70,"context_line":"        # create user"},{"line_number":71,"context_line":"        password \u003d uuid.uuid4().hex"},{"line_number":72,"context_line":"        user \u003d self._get_user_dict(password)"}],"source_content_type":"text/x-python","patch_set":15,"id":"7aa08908_f1633e18","line":69,"in_reply_to":"7aa08908_f15e9ee7","updated":"2016-06-13 16:28:12.000000000","message":"Should be good enough for this requirement.","commit_id":"22921f15ba52fe0b88c206b9adec1a04491aa516"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0e90c3849065fdff4afe001a16323f8e87029a97","unresolved":false,"context_lines":[{"line_number":79,"context_line":"        date_after \u003d datetime.datetime.now()"},{"line_number":80,"context_line":"        # test last_auth_at, checking before/after to account for midnight"},{"line_number":81,"context_line":"        user_ref \u003d self._get_user_ref(user_created[\u0027id\u0027])"},{"line_number":82,"context_line":"        if date_before.date() !\u003d date_after.date():"},{"line_number":83,"context_line":"            self.assertEqual(user_ref.local_user.last_auth_at.date(),"},{"line_number":84,"context_line":"                             date_after.date())"},{"line_number":85,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":17,"id":"7aa08908_2ce56808","line":82,"updated":"2016-06-13 17:56:46.000000000","message":"Couldn\u0027t this if/else just check that the date_after is always greater than the user_ref.local_user.last_auth_at.date()?","commit_id":"9512681ba4333e03f780f4ac7fbe4657faf4ed39"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"dd4a84779496bde653a5013a821ed574360848ea","unresolved":false,"context_lines":[{"line_number":79,"context_line":"        date_after \u003d datetime.datetime.now()"},{"line_number":80,"context_line":"        # test last_auth_at, checking before/after to account for midnight"},{"line_number":81,"context_line":"        user_ref \u003d self._get_user_ref(user_created[\u0027id\u0027])"},{"line_number":82,"context_line":"        if date_before.date() !\u003d date_after.date():"},{"line_number":83,"context_line":"            self.assertEqual(user_ref.local_user.last_auth_at.date(),"},{"line_number":84,"context_line":"                             date_after.date())"},{"line_number":85,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":17,"id":"7aa08908_4c26f4ce","line":82,"in_reply_to":"7aa08908_2ce56808","updated":"2016-06-13 18:09:33.000000000","message":"Done","commit_id":"9512681ba4333e03f780f4ac7fbe4657faf4ed39"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"7ff857ad7aa98bc5996116ea4f44d2804dab8ec8","unresolved":false,"context_lines":[{"line_number":79,"context_line":"        date_after \u003d datetime.datetime.now()"},{"line_number":80,"context_line":"        # test last_auth_at, checking before/after to account for midnight"},{"line_number":81,"context_line":"        user_ref \u003d self._get_user_ref(user_created[\u0027id\u0027])"},{"line_number":82,"context_line":"        if date_before.date() !\u003d date_after.date():"},{"line_number":83,"context_line":"            self.assertEqual(user_ref.local_user.last_auth_at.date(),"},{"line_number":84,"context_line":"                             date_after.date())"},{"line_number":85,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":17,"id":"7aa08908_6c7bf0be","line":82,"in_reply_to":"7aa08908_2ce56808","updated":"2016-06-13 18:09:02.000000000","message":"True, and it does feel a little like overkill here.","commit_id":"9512681ba4333e03f780f4ac7fbe4657faf4ed39"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"161c451d2c9b114dd2829eac4923ab7c00a3c5a1","unresolved":false,"context_lines":[{"line_number":78,"context_line":"        # test last_auth_at"},{"line_number":79,"context_line":"        now \u003d datetime.datetime.now()"},{"line_number":80,"context_line":"        user_ref \u003d self._get_user_ref(user_created[\u0027id\u0027])"},{"line_number":81,"context_line":"        self.assertTrue(now.date() \u003e\u003d user_ref.local_user.last_auth_at.date())"},{"line_number":82,"context_line":""},{"line_number":83,"context_line":"    def test_user_disabled_due_to_inactivity(self):"},{"line_number":84,"context_line":"        password \u003d uuid.uuid4().hex"}],"source_content_type":"text/x-python","patch_set":18,"id":"7aa08908_ffe304d6","line":81,"updated":"2016-06-13 18:26:51.000000000","message":"++ thanks!","commit_id":"125df7d58bc8664e979fac2be8f71b9497945907"}],"keystone/tests/unit/identity/test_backend_sql.py":[{"author":{"_account_id":5707,"name":"Henry Nash","email":"henryn@linux.vnet.ibm.com","username":"henry-nash"},"change_message_id":"0c5ec70a3be53a428d24bbb590e9daa1d5f68c16","unresolved":false,"context_lines":[{"line_number":1,"context_line":"# Licensed under the Apache License, Version 2.0 (the \"License\"); you may"},{"line_number":2,"context_line":"# not use this file except in compliance with the License. You may obtain"},{"line_number":3,"context_line":"# a copy of the License at"},{"line_number":4,"context_line":"#"}],"source_content_type":"text/x-python","patch_set":61,"id":"1aa78d24_1ef3c7a0","line":1,"updated":"2016-07-11 20:11:50.000000000","message":"I *think* the idea of the unit test directory structure, is that this file should go in /unit/identity/backends and be called test_sql.py?? That seems to be the structure followed by other components (e.g. resource, assignment etc.)","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8762c0c182dfa57531f07f25d31ffc793cfc9fd3","unresolved":false,"context_lines":[{"line_number":1,"context_line":"# Licensed under the Apache License, Version 2.0 (the \"License\"); you may"},{"line_number":2,"context_line":"# not use this file except in compliance with the License. You may obtain"},{"line_number":3,"context_line":"# a copy of the License at"},{"line_number":4,"context_line":"#"}],"source_content_type":"text/x-python","patch_set":61,"id":"1aa78d24_d0e4273d","line":1,"in_reply_to":"1aa78d24_1ef3c7a0","updated":"2016-07-12 19:29:44.000000000","message":"Looks like that file already exists, I wonder if we should merge the two?","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c883f59c64f9b954a2d929ff55cb4f1aa3bf18fe","unresolved":false,"context_lines":[{"line_number":40,"context_line":"        # create user and set last_active_at beyond the max"},{"line_number":41,"context_line":"        last_active_at \u003d ("},{"line_number":42,"context_line":"            datetime.datetime.utcnow() -"},{"line_number":43,"context_line":"            datetime.timedelta(days\u003dself.max_inactive_days + 1))"},{"line_number":44,"context_line":"        user \u003d self._create_user(self.user_dict, last_active_at.date())"},{"line_number":45,"context_line":"        self.assertRaises(exception.UserDisabled,"},{"line_number":46,"context_line":"                          self.identity_api.authenticate,"}],"source_content_type":"text/x-python","patch_set":61,"id":"3aaa91ec_2a0fe837","line":43,"updated":"2016-06-30 16:41:05.000000000","message":"On L65, etc, you call .date() here","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8762c0c182dfa57531f07f25d31ffc793cfc9fd3","unresolved":false,"context_lines":[{"line_number":40,"context_line":"        # create user and set last_active_at beyond the max"},{"line_number":41,"context_line":"        last_active_at \u003d ("},{"line_number":42,"context_line":"            datetime.datetime.utcnow() -"},{"line_number":43,"context_line":"            datetime.timedelta(days\u003dself.max_inactive_days + 1))"},{"line_number":44,"context_line":"        user \u003d self._create_user(self.user_dict, last_active_at.date())"},{"line_number":45,"context_line":"        self.assertRaises(exception.UserDisabled,"},{"line_number":46,"context_line":"                          self.identity_api.authenticate,"}],"source_content_type":"text/x-python","patch_set":61,"id":"1aa78d24_f06323af","line":43,"in_reply_to":"3aaa91ec_2a0fe837","updated":"2016-07-12 19:29:44.000000000","message":"Done","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"c883f59c64f9b954a2d929ff55cb4f1aa3bf18fe","unresolved":false,"context_lines":[{"line_number":62,"context_line":"        # create user and set last_active_at just below the max"},{"line_number":63,"context_line":"        last_active_at \u003d ("},{"line_number":64,"context_line":"            datetime.datetime.utcnow() -"},{"line_number":65,"context_line":"            datetime.timedelta(days\u003dself.max_inactive_days - 1)).date()"},{"line_number":66,"context_line":"        user \u003d self._create_user(self.user_dict, last_active_at)"},{"line_number":67,"context_line":"        user \u003d self.identity_api.authenticate(context\u003d{},"},{"line_number":68,"context_line":"                                              user_id\u003duser[\u0027id\u0027],"}],"source_content_type":"text/x-python","patch_set":61,"id":"3aaa91ec_e556b1cc","line":65,"updated":"2016-06-30 16:41:05.000000000","message":"Example of using freezegun in keystone:\n\n  https://review.openstack.org/#/c/227995/17/keystone/tests/unit/test_v3_os_revoke.py","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"},{"author":{"_account_id":6460,"name":"Brad Topol","email":"btopol@us.ibm.com","username":"btopol"},"change_message_id":"fbd7fee6f4c6b9ae6bd11e8a6531652e9ce192e8","unresolved":false,"context_lines":[{"line_number":62,"context_line":"        # create user and set last_active_at just below the max"},{"line_number":63,"context_line":"        last_active_at \u003d ("},{"line_number":64,"context_line":"            datetime.datetime.utcnow() -"},{"line_number":65,"context_line":"            datetime.timedelta(days\u003dself.max_inactive_days - 1)).date()"},{"line_number":66,"context_line":"        user \u003d self._create_user(self.user_dict, last_active_at)"},{"line_number":67,"context_line":"        user \u003d self.identity_api.authenticate(context\u003d{},"},{"line_number":68,"context_line":"                                              user_id\u003duser[\u0027id\u0027],"}],"source_content_type":"text/x-python","patch_set":61,"id":"1aa78d24_1d9d5666","line":65,"in_reply_to":"3aaa91ec_e556b1cc","updated":"2016-07-11 21:38:40.000000000","message":"freezegun: coolest. python. package. name. ever!","commit_id":"e3a5b61d51ffb4bdabd132e047d1a9963cdbc65a"}],"keystone/tests/unit/identity/test_backends.py":[{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"097622b4d116494ffcbafa2b77e22f972aba220a","unresolved":false,"context_lines":[{"line_number":115,"context_line":"            user_id\u003dself.user_sna[\u0027id\u0027],"},{"line_number":116,"context_line":"            password\u003dself.user_sna[\u0027password\u0027])"},{"line_number":117,"context_line":"        user_ref \u003d self._get_user_ref(user_ref[\u0027id\u0027])"},{"line_number":118,"context_line":"        self.assertTrue(now.date() \u003e\u003d user_ref.last_auth_at.date())"},{"line_number":119,"context_line":""},{"line_number":120,"context_line":"    def test_last_auth_date_when_disable_inactive_is_zero(self):"},{"line_number":121,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"}],"source_content_type":"text/x-python","patch_set":29,"id":"7aa08908_0d41d139","line":118,"updated":"2016-06-16 17:38:02.000000000","message":"This will return better error messages in the event of an assertion failure:\n  https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.assertGreaterEqual\n\nYou could also use freezegun (it\u0027s in global requirements, but we don\u0027t use it in keystone yet) in this test to stop the clock, and ensure that the dates are actually equal.","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"ff328756f5b9f6835ef19cc477942124ae391363","unresolved":false,"context_lines":[{"line_number":115,"context_line":"            user_id\u003dself.user_sna[\u0027id\u0027],"},{"line_number":116,"context_line":"            password\u003dself.user_sna[\u0027password\u0027])"},{"line_number":117,"context_line":"        user_ref \u003d self._get_user_ref(user_ref[\u0027id\u0027])"},{"line_number":118,"context_line":"        self.assertTrue(now.date() \u003e\u003d user_ref.last_auth_at.date())"},{"line_number":119,"context_line":""},{"line_number":120,"context_line":"    def test_last_auth_date_when_disable_inactive_is_zero(self):"},{"line_number":121,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"}],"source_content_type":"text/x-python","patch_set":29,"id":"3aaa91ec_843bf3d0","line":118,"in_reply_to":"7aa08908_0d41d139","updated":"2016-06-22 17:29:26.000000000","message":"Done","commit_id":"71fab1dcfaeb2a979547ad8c48331b7129506f02"}]}
