)]}'
{"keystone/common/config.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3d78ea3a19269443a3c2eb5bac83884507857bd5","unresolved":false,"context_lines":[{"line_number":1092,"context_line":"                   default\u003dNone,"},{"line_number":1093,"context_line":"                   help\u003d\u0027Description of the password regular expression \u0027"},{"line_number":1094,"context_line":"                        \u0027requirements to be returned in the response when \u0027"},{"line_number":1095,"context_line":"                        \u0027validation fails.\u0027)"},{"line_number":1096,"context_line":"    ]"},{"line_number":1097,"context_line":"}"},{"line_number":1098,"context_line":""}],"source_content_type":"text/x-python","patch_set":42,"id":"5a9d85d2_2b83f505","line":1095,"updated":"2016-06-21 02:57:29.000000000","message":"++","commit_id":"6d48b36a05bf49ed5fd17a008feb5a86f806a38f"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"9d3db84f2e4a4c548963fa9e4fffdfa8172a3867","unresolved":false,"context_lines":[{"line_number":1088,"context_line":"                        \u0027at least 1 letter, 1 digit, and have a minimum \u0027"},{"line_number":1089,"context_line":"                        \u0027length of 7 characters: \u0027"},{"line_number":1090,"context_line":"                        \u0027^(?\u003d.*\\d)(?\u003d.*[a-zA-Z]).{7,}$\u0027),"},{"line_number":1091,"context_line":"        cfg.StrOpt(\u0027password_regex_desc\u0027,"},{"line_number":1092,"context_line":"                   default\u003dNone,"},{"line_number":1093,"context_line":"                   help\u003d\u0027Description of the password regular expression \u0027"},{"line_number":1094,"context_line":"                        \u0027requirements to be returned in the response when \u0027"}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_a6b4f781","line":1091,"updated":"2016-06-23 18:18:06.000000000","message":"Spell out description?","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c46770b31e817774ed445a28492c2107bcd6065a","unresolved":false,"context_lines":[{"line_number":1088,"context_line":"                        \u0027at least 1 letter, 1 digit, and have a minimum \u0027"},{"line_number":1089,"context_line":"                        \u0027length of 7 characters: \u0027"},{"line_number":1090,"context_line":"                        \u0027^(?\u003d.*\\d)(?\u003d.*[a-zA-Z]).{7,}$\u0027),"},{"line_number":1091,"context_line":"        cfg.StrOpt(\u0027password_regex_desc\u0027,"},{"line_number":1092,"context_line":"                   default\u003dNone,"},{"line_number":1093,"context_line":"                   help\u003d\u0027Description of the password regular expression \u0027"},{"line_number":1094,"context_line":"                        \u0027requirements to be returned in the response when \u0027"}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_2e54591f","line":1091,"in_reply_to":"3aaa91ec_a6b4f781","updated":"2016-06-23 19:01:16.000000000","message":"Done","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"9d3db84f2e4a4c548963fa9e4fffdfa8172a3867","unresolved":false,"context_lines":[{"line_number":1090,"context_line":"                        \u0027^(?\u003d.*\\d)(?\u003d.*[a-zA-Z]).{7,}$\u0027),"},{"line_number":1091,"context_line":"        cfg.StrOpt(\u0027password_regex_desc\u0027,"},{"line_number":1092,"context_line":"                   default\u003dNone,"},{"line_number":1093,"context_line":"                   help\u003d\u0027Description of the password regular expression \u0027"},{"line_number":1094,"context_line":"                        \u0027requirements to be returned in the response when \u0027"},{"line_number":1095,"context_line":"                        \u0027validation fails.\u0027)"},{"line_number":1096,"context_line":"    ]"}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_e660bf03","line":1093,"updated":"2016-06-23 18:18:06.000000000","message":"Write this as instructions to operators, rather than a description of a description, which is a little bit of a mind bender. \"Describe your password regular expression here in language for humans. If a password fails to match the regular expression, the contents of this configuration variable will be returned to users to explain why their requested password was insufficient.\"","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c46770b31e817774ed445a28492c2107bcd6065a","unresolved":false,"context_lines":[{"line_number":1090,"context_line":"                        \u0027^(?\u003d.*\\d)(?\u003d.*[a-zA-Z]).{7,}$\u0027),"},{"line_number":1091,"context_line":"        cfg.StrOpt(\u0027password_regex_desc\u0027,"},{"line_number":1092,"context_line":"                   default\u003dNone,"},{"line_number":1093,"context_line":"                   help\u003d\u0027Description of the password regular expression \u0027"},{"line_number":1094,"context_line":"                        \u0027requirements to be returned in the response when \u0027"},{"line_number":1095,"context_line":"                        \u0027validation fails.\u0027)"},{"line_number":1096,"context_line":"    ]"}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_4e056512","line":1093,"in_reply_to":"3aaa91ec_e660bf03","updated":"2016-06-23 19:01:16.000000000","message":"Makes sense.  Thanks.","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"9df8c4ce3e113eec7afa727ca4fbad7701dcfdc6","unresolved":false,"context_lines":[{"line_number":1092,"context_line":"                   default\u003dNone,"},{"line_number":1093,"context_line":"                   help\u003d\u0027Description of the password regular expression \u0027"},{"line_number":1094,"context_line":"                        \u0027requirements to be returned in the response when \u0027"},{"line_number":1095,"context_line":"                        \u0027validation fails.\u0027)"},{"line_number":1096,"context_line":"    ]"},{"line_number":1097,"context_line":"}"},{"line_number":1098,"context_line":""}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_9aa4eaa6","line":1095,"updated":"2016-06-22 18:24:22.000000000","message":"Random question, is there a way to make this required if password_regex is defined?","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"9d3db84f2e4a4c548963fa9e4fffdfa8172a3867","unresolved":false,"context_lines":[{"line_number":1092,"context_line":"                   default\u003dNone,"},{"line_number":1093,"context_line":"                   help\u003d\u0027Description of the password regular expression \u0027"},{"line_number":1094,"context_line":"                        \u0027requirements to be returned in the response when \u0027"},{"line_number":1095,"context_line":"                        \u0027validation fails.\u0027)"},{"line_number":1096,"context_line":"    ]"},{"line_number":1097,"context_line":"}"},{"line_number":1098,"context_line":""}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_cd767ed6","line":1095,"in_reply_to":"3aaa91ec_9aa4eaa6","updated":"2016-06-23 18:18:06.000000000","message":"Not with oslo.config, but it\u0027d be a good keystone-manage doctor check. We could also include a basic, terrible default here like \"Your password strength is not sufficient.\"","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"ef0248e296e55433b9e09702ca34f3dd67efa3a7","unresolved":false,"context_lines":[{"line_number":1098,"context_line":"                        \u0027match the regular expression, the contents of this \u0027"},{"line_number":1099,"context_line":"                        \u0027configuration variable will be returned to users \u0027"},{"line_number":1100,"context_line":"                        \u0027to explain why their requested password was \u0027"},{"line_number":1101,"context_line":"                        \u0027insufficient.\u0027)"},{"line_number":1102,"context_line":"    ]"},{"line_number":1103,"context_line":"}"},{"line_number":1104,"context_line":""}],"source_content_type":"text/x-python","patch_set":47,"id":"3aaa91ec_77c11054","line":1101,"updated":"2016-06-23 19:47:28.000000000","message":"++ much better.","commit_id":"c4f2f44e6b1470a30139305e6426b9d2b9d73875"}],"keystone/common/validation/validators.py":[{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"dbc98e46a9c0f5b5177527c3e91e545f470b6040","unresolved":false,"context_lines":[{"line_number":11,"context_line":"# under the License."},{"line_number":12,"context_line":"\"\"\"Internal implementation of request body validating middleware.\"\"\""},{"line_number":13,"context_line":""},{"line_number":14,"context_line":"import jsonschema"},{"line_number":15,"context_line":"import re"},{"line_number":16,"context_line":""},{"line_number":17,"context_line":"from oslo_config import cfg"}],"source_content_type":"text/x-python","patch_set":26,"id":"7aa08908_1e176ea4","line":14,"updated":"2016-06-06 12:37:53.000000000","message":"This is third-party so the sections need to rearranged.","commit_id":"4a61acfb0665df3b7f447ebbe45c85fc24981c4e"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"32a6143ce33720762d9b6d8e260fdb4b5202f2f6","unresolved":false,"context_lines":[{"line_number":11,"context_line":"# under the License."},{"line_number":12,"context_line":"\"\"\"Internal implementation of request body validating middleware.\"\"\""},{"line_number":13,"context_line":""},{"line_number":14,"context_line":"import jsonschema"},{"line_number":15,"context_line":"import re"},{"line_number":16,"context_line":""},{"line_number":17,"context_line":"from oslo_config import cfg"}],"source_content_type":"text/x-python","patch_set":26,"id":"7aa08908_1a21334e","line":14,"in_reply_to":"7aa08908_1e176ea4","updated":"2016-06-06 14:03:11.000000000","message":"Done","commit_id":"4a61acfb0665df3b7f447ebbe45c85fc24981c4e"},{"author":{"_account_id":6460,"name":"Brad Topol","email":"btopol@us.ibm.com","username":"btopol"},"change_message_id":"9366640b9d330a6be2c74dd79a9e669598eb915d","unresolved":false,"context_lines":[{"line_number":27,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":28,"context_line":"class PasswordValidator(object):"},{"line_number":29,"context_line":"    def validate(self, password):"},{"line_number":30,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":31,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":32,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":33,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"}],"source_content_type":"text/x-python","patch_set":30,"id":"7aa08908_2cc73697","line":30,"updated":"2016-06-07 23:07:14.000000000","message":"I very much appreciate the comment in the sample config file that shows how to do a 7 char minimum with at least decimal and one alphabetic char.  I know that came in a previous patch but its still much appreciated!","commit_id":"1049bc2c2befbf74ab83d0c1808ebe8245536e3f"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"3fb4ccca8bb277c18871e1d6bf46a99e4d866000","unresolved":false,"context_lines":[{"line_number":14,"context_line":"import re"},{"line_number":15,"context_line":""},{"line_number":16,"context_line":"import jsonschema"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from oslo_config import cfg"},{"line_number":19,"context_line":""},{"line_number":20,"context_line":"from keystone import exception"}],"source_content_type":"text/x-python","patch_set":34,"id":"7aa08908_49c0427d","line":17,"updated":"2016-06-09 13:28:45.000000000","message":"(nit) no newline","commit_id":"fe1eb8a43d7d9d0c132fdde04d0f9456dfca867a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"2b04023f4be432364657f86cdb88a6b2146c25fa","unresolved":false,"context_lines":[{"line_number":14,"context_line":"import re"},{"line_number":15,"context_line":""},{"line_number":16,"context_line":"import jsonschema"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from oslo_config import cfg"},{"line_number":19,"context_line":""},{"line_number":20,"context_line":"from keystone import exception"}],"source_content_type":"text/x-python","patch_set":34,"id":"7aa08908_a759f490","line":17,"in_reply_to":"7aa08908_49c0427d","updated":"2016-06-09 20:43:37.000000000","message":"Done","commit_id":"fe1eb8a43d7d9d0c132fdde04d0f9456dfca867a"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"3fb4ccca8bb277c18871e1d6bf46a99e4d866000","unresolved":false,"context_lines":[{"line_number":26,"context_line":""},{"line_number":27,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":28,"context_line":"class PasswordValidator(object):"},{"line_number":29,"context_line":"    def validate(self, password):"},{"line_number":30,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":31,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":32,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"}],"source_content_type":"text/x-python","patch_set":34,"id":"7aa08908_09711ae1","line":29,"updated":"2016-06-09 13:28:45.000000000","message":"I dislike stateless objects. If this is to be it\u0027s own object it would be nice to have the pattern passed in to a constructor, otherwise it can just be a function.","commit_id":"fe1eb8a43d7d9d0c132fdde04d0f9456dfca867a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"17f9bbfc73ceb5d348f7194557d1465d616ba5f1","unresolved":false,"context_lines":[{"line_number":26,"context_line":""},{"line_number":27,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":28,"context_line":"class PasswordValidator(object):"},{"line_number":29,"context_line":"    def validate(self, password):"},{"line_number":30,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":31,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":32,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"}],"source_content_type":"text/x-python","patch_set":34,"id":"7aa08908_c42ed7f9","line":29,"in_reply_to":"7aa08908_09711ae1","updated":"2016-06-09 13:40:40.000000000","message":"I dislike just functions :)  I suppose this could be a static method.","commit_id":"fe1eb8a43d7d9d0c132fdde04d0f9456dfca867a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"b07e8bb358a897334ee222072d77b18cbc450b1d","unresolved":false,"context_lines":[{"line_number":26,"context_line":""},{"line_number":27,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":28,"context_line":"class PasswordValidator(object):"},{"line_number":29,"context_line":"    def validate(self, password):"},{"line_number":30,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":31,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":32,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"}],"source_content_type":"text/x-python","patch_set":34,"id":"7aa08908_9ca6e751","line":29,"in_reply_to":"7aa08908_3f5a4851","updated":"2016-06-09 14:22:24.000000000","message":"I like functions to be grouped within classes and I\u0027m okay with small, well focused classes.","commit_id":"fe1eb8a43d7d9d0c132fdde04d0f9456dfca867a"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"bbd6537ac580c0fd84c4dae86bdf657839437828","unresolved":false,"context_lines":[{"line_number":26,"context_line":""},{"line_number":27,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":28,"context_line":"class PasswordValidator(object):"},{"line_number":29,"context_line":"    def validate(self, password):"},{"line_number":30,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":31,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":32,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"}],"source_content_type":"text/x-python","patch_set":34,"id":"7aa08908_3f5a4851","line":29,"in_reply_to":"7aa08908_c42ed7f9","updated":"2016-06-09 13:55:11.000000000","message":"Object().f() \u003d\u003d f()","commit_id":"fe1eb8a43d7d9d0c132fdde04d0f9456dfca867a"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"4ccf80dd9a8ed1c2242b558244fd07fc1945b453","unresolved":false,"context_lines":[{"line_number":23,"context_line":"CONF \u003d cfg.CONF"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":27,"context_line":"class PasswordValidator(object):"},{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_1edc6445","line":26,"updated":"2016-06-16 15:53:09.000000000","message":"I\u0027d be curious to see what json schema\u0027s error messages look like when a password doesn\u0027t match a regex... json schema\u0027s errors are usually super vague, so it might be a question of whether we can provide a better user experience by implementing this (important) bit of validation ourselves.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":2903,"name":"Morgan Fainberg","email":"morgan.fainberg@gmail.com","username":"mdrnstm"},"change_message_id":"0e9fa23ced1a3ea7bf940c66fc5cc8c54cfac734","unresolved":false,"context_lines":[{"line_number":26,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":27,"context_line":"class PasswordValidator(object):"},{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":31,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_31bfd1fd","line":29,"range":{"start_line":29,"start_character":18,"end_line":29,"end_character":57},"updated":"2016-06-10 16:43:52.000000000","message":"Is a regex really the direction we want to go here? I worry it is very prone to failure (regex crafting is flexible but often not easy). Perhaps we should lean on cracklib instead of needing to re-invent this validation?","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"49a8c891fc7a9ec6909b5317c1e31f137167b6d2","unresolved":false,"context_lines":[{"line_number":26,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":27,"context_line":"class PasswordValidator(object):"},{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":31,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_3e9de057","line":29,"range":{"start_line":29,"start_character":18,"end_line":29,"end_character":57},"in_reply_to":"7aa08908_31bfd1fd","updated":"2016-06-16 15:57:36.000000000","message":"Is cracklib something that\u0027s supported in the linux dists we support? If we really needed to do this then we would probably need to make the Validator something that can be specified in the config file like hash algorithms.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"4ccf80dd9a8ed1c2242b558244fd07fc1945b453","unresolved":false,"context_lines":[{"line_number":26,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":27,"context_line":"class PasswordValidator(object):"},{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":31,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_be06f0ca","line":29,"in_reply_to":"7aa08908_31bfd1fd","updated":"2016-06-16 15:53:09.000000000","message":"Regular expressions to validate passwords against various standards are well documented and easy to find. Using regex here allows deployers maximum flexibility. That said, we of course have two problems now.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a6f664f2de8bae41949fb0afc1084653e968d83d","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":31,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_1396c73d","line":31,"updated":"2016-06-13 19:46:17.000000000","message":"Typically when an system requires you to have a certain level of password strength - it will give you a set of rules. Like: \n\n    - must be at least 8 characters long\n    - must have one capitalized character\n    - must contain one number\n\netc... do we have a way of providing that information here? The user doesn\u0027t have any way to know what things their password is missing when they see this error message.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"49a8c891fc7a9ec6909b5317c1e31f137167b6d2","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":31,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_7eec88bb","line":31,"in_reply_to":"7aa08908_1396c73d","updated":"2016-06-16 15:57:36.000000000","message":"++","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"0ffd0d263af506ef1f32fa46f9acb5efe20e0637","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":31,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_f94775b9","line":31,"in_reply_to":"7aa08908_1396c73d","updated":"2016-06-14 13:56:52.000000000","message":"Currently we don\u0027t, in that we\u0027re only relying on the regex in the configuration.  We could also have operators input the rules description in HTML somewhere (config maybe) and return that in a new API and/or the error message.  What are your thoughts on this?","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c24899538df628ccdaadc7b4a65789ee7dcc36e3","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":31,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""}],"source_content_type":"text/x-python","patch_set":37,"id":"5a9d85d2_ebaf49e1","line":31,"in_reply_to":"7aa08908_7e700841","updated":"2016-06-20 16:08:12.000000000","message":"Done","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"7a03aaa0df3d1d2b7fdadaee2373a5f6399ab804","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":31,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_91e68542","line":31,"in_reply_to":"7aa08908_7e700841","updated":"2016-06-16 15:57:38.000000000","message":"Yeah - that\u0027s definitely something we can do. We would really have to rely on the ability of the operator to test and document their regular expression to provide good u-x.\n\nPossibility of a big foot gun?","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"ef9090982db392195f7606e3f5e310666406bb0d","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":31,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_914a0554","line":31,"in_reply_to":"7aa08908_7e700841","updated":"2016-06-16 15:55:40.000000000","message":"Yeah, was thinking that as well.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"4ccf80dd9a8ed1c2242b558244fd07fc1945b453","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, password):"},{"line_number":31,"context_line":"            detail \u003d _(\u0027Invalid password: does not match password regex\u0027)"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_7e700841","line":31,"in_reply_to":"7aa08908_f94775b9","updated":"2016-06-16 15:53:09.000000000","message":"Maybe another configuration option should be a human readable description of the password requirements, so that it can be returned in this error message?","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3d78ea3a19269443a3c2eb5bac83884507857bd5","unresolved":false,"context_lines":[{"line_number":26,"context_line":"class PasswordValidator(object):"},{"line_number":27,"context_line":"    def validate(self, password):"},{"line_number":28,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":29,"context_line":"        if pattern and not re.match(pattern, str(password)):"},{"line_number":30,"context_line":"            pattern_desc \u003d CONF.security_compliance.password_regex_desc"},{"line_number":31,"context_line":"            detail \u003d _(\"The password does not meet the requirements: \""},{"line_number":32,"context_line":"                       \"%(message)s\") % {\u0027message\u0027: pattern_desc}"}],"source_content_type":"text/x-python","patch_set":42,"id":"5a9d85d2_4bcda15c","line":29,"updated":"2016-06-21 02:57:29.000000000","message":"I thought I remember a way to pass a regular expression to a jsonschema Validator and have it validate like you would any other schema. \n\nSomething that leverages `is_regex()` here I think? Then we might be able to roll the validation into the decorator like we do with the rest of the create_user and update_user calls (versus having a separate call to the validation module just to validate passwords).\n\nhttp://python-jsonschema.readthedocs.io/en/latest/_modules/jsonschema/_format/?highlight\u003dregex","commit_id":"6d48b36a05bf49ed5fd17a008feb5a86f806a38f"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"fec97c97465908349f87674b51f3a14e08c2f43c","unresolved":false,"context_lines":[{"line_number":26,"context_line":"class PasswordValidator(object):"},{"line_number":27,"context_line":"    def validate(self, password):"},{"line_number":28,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":29,"context_line":"        if pattern and not re.match(pattern, str(password)):"},{"line_number":30,"context_line":"            pattern_desc \u003d CONF.security_compliance.password_regex_desc"},{"line_number":31,"context_line":"            detail \u003d _(\"The password does not meet the requirements: \""},{"line_number":32,"context_line":"                       \"%(message)s\") % {\u0027message\u0027: pattern_desc}"}],"source_content_type":"text/x-python","patch_set":42,"id":"3aaa91ec_22a50f04","line":29,"in_reply_to":"5a9d85d2_4bcda15c","updated":"2016-06-21 19:42:41.000000000","message":"Ah ha - in think about this a little bit deeper the reason why we can\u0027t pass the regular expression to the validator is because we can\u0027t enforce when configuration is loaded (import errors). This *is* something we should be able to do once Dolph\u0027s work is done though, right?\n\nWe could maybe add a comment to this method saying to collapse it into the normal validation path once Dolph\u0027s work lands.","commit_id":"6d48b36a05bf49ed5fd17a008feb5a86f806a38f"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"9df8c4ce3e113eec7afa727ca4fbad7701dcfdc6","unresolved":false,"context_lines":[{"line_number":23,"context_line":"CONF \u003d cfg.CONF"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":27,"context_line":"class PasswordValidator(object):"},{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_9f785cc6","line":26,"updated":"2016-06-22 18:24:22.000000000","message":"++","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"9d3db84f2e4a4c548963fa9e4fffdfa8172a3867","unresolved":false,"context_lines":[{"line_number":27,"context_line":"class PasswordValidator(object):"},{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, str(password)):"},{"line_number":31,"context_line":"            pattern_desc \u003d CONF.security_compliance.password_regex_desc"},{"line_number":32,"context_line":"            detail \u003d _(\"The password does not meet the requirements: \""},{"line_number":33,"context_line":"                       \"%(message)s\") % {\u0027message\u0027: pattern_desc}"}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_795ab02b","line":30,"updated":"2016-06-23 18:18:06.000000000","message":"After discussing, it sounded like you needed to check isinstance(password, basestring) here, rather than casting the input to a binary type in py2.\n\nMy original comment: Is this Python 3 compatible? I would think this should be the same across py 2 and py 3, so this would be six.binary_type (what you have) or, I would think six.text_type (but it looks like re.match expects a binary type, so I\u0027m probably wrong).","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"350d64c41b548c9204035f4b33d9ceb4c92758cb","unresolved":false,"context_lines":[{"line_number":27,"context_line":"class PasswordValidator(object):"},{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, str(password)):"},{"line_number":31,"context_line":"            pattern_desc \u003d CONF.security_compliance.password_regex_desc"},{"line_number":32,"context_line":"            detail \u003d _(\"The password does not meet the requirements: \""},{"line_number":33,"context_line":"                       \"%(message)s\") % {\u0027message\u0027: pattern_desc}"}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_070acc89","line":30,"in_reply_to":"3aaa91ec_717be08c","updated":"2016-06-26 08:42:01.000000000","message":"Actually changed this to check isinstance(password, six.string_types) in the latest branch in order to be py2 and p3 compatible.  \n\nre.match expects a string for the 2nd argument, so I think having a check makes sense here.","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c46770b31e817774ed445a28492c2107bcd6065a","unresolved":false,"context_lines":[{"line_number":27,"context_line":"class PasswordValidator(object):"},{"line_number":28,"context_line":"    def validate(self, password):"},{"line_number":29,"context_line":"        pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"        if pattern and not re.match(pattern, str(password)):"},{"line_number":31,"context_line":"            pattern_desc \u003d CONF.security_compliance.password_regex_desc"},{"line_number":32,"context_line":"            detail \u003d _(\"The password does not meet the requirements: \""},{"line_number":33,"context_line":"                       \"%(message)s\") % {\u0027message\u0027: pattern_desc}"}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_717be08c","line":30,"in_reply_to":"3aaa91ec_795ab02b","updated":"2016-06-23 19:01:16.000000000","message":"Added a check for isinstance(password, basestring) here.  Seems to still make sense.","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"ef0248e296e55433b9e09702ca34f3dd67efa3a7","unresolved":false,"context_lines":[{"line_number":27,"context_line":"def validate_password(password):"},{"line_number":28,"context_line":"    pattern \u003d CONF.security_compliance.password_regex"},{"line_number":29,"context_line":"    if pattern:"},{"line_number":30,"context_line":"        if not isinstance(password, basestring):"},{"line_number":31,"context_line":"            detail \u003d _(\"Password must be basestring\")"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":33,"context_line":"        elif not re.match(pattern, password):"}],"source_content_type":"text/x-python","patch_set":47,"id":"3aaa91ec_f77b4026","line":30,"updated":"2016-06-23 19:47:28.000000000","message":"Do we have a test that exercises the basestring check?","commit_id":"c4f2f44e6b1470a30139305e6426b9d2b9d73875"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c7d385e46623eb4dfe1d182882edd164004579e4","unresolved":false,"context_lines":[{"line_number":27,"context_line":"def validate_password(password):"},{"line_number":28,"context_line":"    pattern \u003d CONF.security_compliance.password_regex"},{"line_number":29,"context_line":"    if pattern:"},{"line_number":30,"context_line":"        if not isinstance(password, basestring):"},{"line_number":31,"context_line":"            detail \u003d _(\"Password must be basestring\")"},{"line_number":32,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":33,"context_line":"        elif not re.match(pattern, password):"}],"source_content_type":"text/x-python","patch_set":47,"id":"3aaa91ec_b715384a","line":30,"in_reply_to":"3aaa91ec_f77b4026","updated":"2016-06-23 20:00:14.000000000","message":"Yes, it\u0027s handled here (ln 2189):\nhttps://review.openstack.org/#/c/320586/47/keystone/tests/unit/test_validation.py","commit_id":"c4f2f44e6b1470a30139305e6426b9d2b9d73875"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"642d3e8b65f965adb3f401ca35f2dd834a4ef6af","unresolved":false,"context_lines":[{"line_number":24,"context_line":"CONF \u003d cfg.CONF"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":""},{"line_number":27,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":28,"context_line":"def validate_password(password):"},{"line_number":29,"context_line":"    pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"    if pattern:"}],"source_content_type":"text/x-python","patch_set":55,"id":"3aaa91ec_2feb28b2","line":27,"updated":"2016-06-27 20:21:06.000000000","message":"now that dolph\u0027s patch merged, we should be able to import the password regular expression from configuration and apply it in the user schema.","commit_id":"5e6f2514f1ac9b4fa4310c825a1ed551a0f3cf2d"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"cce72536355f671d02f7169eaa86cfc0b88315af","unresolved":false,"context_lines":[{"line_number":24,"context_line":"CONF \u003d cfg.CONF"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":""},{"line_number":27,"context_line":"# TODO(rderose): extend schema validation and add this check there"},{"line_number":28,"context_line":"def validate_password(password):"},{"line_number":29,"context_line":"    pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"    if pattern:"}],"source_content_type":"text/x-python","patch_set":55,"id":"3aaa91ec_b01c9414","line":27,"in_reply_to":"3aaa91ec_2feb28b2","updated":"2016-06-27 21:00:57.000000000","message":"Come on, this was so done :)  And there is an advantage as this covers v2 and v3, whereas schema validation only covers v3.  Extending the validation could be done later on.","commit_id":"5e6f2514f1ac9b4fa4310c825a1ed551a0f3cf2d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8cfcb03e4a66e027839e1be6597fd9ade71338c0","unresolved":false,"context_lines":[{"line_number":28,"context_line":"def validate_password(password):"},{"line_number":29,"context_line":"    pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"    if pattern:"},{"line_number":31,"context_line":"        if not isinstance(password, six.string_types):"},{"line_number":32,"context_line":"            detail \u003d _(\"Password must be a string type\")"},{"line_number":33,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":34,"context_line":"        elif not re.match(pattern, password):"}],"source_content_type":"text/x-python","patch_set":55,"id":"1aa78d24_71e775e4","line":31,"updated":"2016-07-11 16:20:46.000000000","message":"Isn\u0027t this enforced by using cfg.StrOpt ? Using cfg.StrOpt will cast whatever the value is to be a string.\n\nhttp://cdn.pasteraw.com/144mufonfm5xrz74jyp6uyskfezy8kv","commit_id":"5e6f2514f1ac9b4fa4310c825a1ed551a0f3cf2d"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"621e6d30fbd59fa82d435e91707b26aa82752503","unresolved":false,"context_lines":[{"line_number":28,"context_line":"def validate_password(password):"},{"line_number":29,"context_line":"    pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"    if pattern:"},{"line_number":31,"context_line":"        if not isinstance(password, six.string_types):"},{"line_number":32,"context_line":"            detail \u003d _(\"Password must be a string type\")"},{"line_number":33,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":34,"context_line":"        elif not re.match(pattern, password):"}],"source_content_type":"text/x-python","patch_set":55,"id":"1aa78d24_369aec6e","line":31,"in_reply_to":"1aa78d24_2c7914fd","updated":"2016-07-12 01:58:51.000000000","message":"Added a check for validating the regular expression.","commit_id":"5e6f2514f1ac9b4fa4310c825a1ed551a0f3cf2d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"0739416e4ce68245774e2623ba5cbcc8ddb7ba40","unresolved":false,"context_lines":[{"line_number":28,"context_line":"def validate_password(password):"},{"line_number":29,"context_line":"    pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"    if pattern:"},{"line_number":31,"context_line":"        if not isinstance(password, six.string_types):"},{"line_number":32,"context_line":"            detail \u003d _(\"Password must be a string type\")"},{"line_number":33,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":34,"context_line":"        elif not re.match(pattern, password):"}],"source_content_type":"text/x-python","patch_set":55,"id":"1aa78d24_2c7914fd","line":31,"in_reply_to":"1aa78d24_71e775e4","updated":"2016-07-11 16:23:10.000000000","message":"Should we have a way to validate the regular expression? If the regular expression isn\u0027t valid then the user will get an error saying that the password doesn\u0027t meet the requirements, even though it might.","commit_id":"5e6f2514f1ac9b4fa4310c825a1ed551a0f3cf2d"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c49090430185d32671e5d1d6ca63a0bbb3755335","unresolved":false,"context_lines":[{"line_number":28,"context_line":"def validate_password(password):"},{"line_number":29,"context_line":"    pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"    if pattern:"},{"line_number":31,"context_line":"        if not isinstance(password, six.string_types):"},{"line_number":32,"context_line":"            detail \u003d _(\"Password must be a string type\")"},{"line_number":33,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":34,"context_line":"        elif not re.match(pattern, password):"}],"source_content_type":"text/x-python","patch_set":55,"id":"1aa78d24_ad5a0ff0","line":31,"in_reply_to":"1aa78d24_71e775e4","updated":"2016-07-12 00:07:01.000000000","message":"This is ensuring the password is a string; not the pattern.","commit_id":"5e6f2514f1ac9b4fa4310c825a1ed551a0f3cf2d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"3d62ac209973a4410c222010bc551d3a9bb7f9d4","unresolved":false,"context_lines":[{"line_number":28,"context_line":"def validate_password(password):"},{"line_number":29,"context_line":"    pattern \u003d CONF.security_compliance.password_regex"},{"line_number":30,"context_line":"    if pattern:"},{"line_number":31,"context_line":"        if not isinstance(password, six.string_types):"},{"line_number":32,"context_line":"            detail \u003d _(\"Password must be a string type\")"},{"line_number":33,"context_line":"            raise exception.PasswordValidationError(detail\u003ddetail)"},{"line_number":34,"context_line":"        elif not re.match(pattern, password):"}],"source_content_type":"text/x-python","patch_set":55,"id":"1aa78d24_9ade3e6b","line":31,"in_reply_to":"1aa78d24_ad5a0ff0","updated":"2016-07-12 15:10:33.000000000","message":"Ah ha - you\u0027re right... password !\u003d pattern","commit_id":"5e6f2514f1ac9b4fa4310c825a1ed551a0f3cf2d"}],"keystone/exception.py":[{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"4ccf80dd9a8ed1c2242b558244fd07fc1945b453","unresolved":false,"context_lines":[{"line_number":94,"context_line":""},{"line_number":95,"context_line":""},{"line_number":96,"context_line":"class PasswordValidationError(ValidationError):"},{"line_number":97,"context_line":"    message_format \u003d _(\"%(detail)s\")"},{"line_number":98,"context_line":""},{"line_number":99,"context_line":""},{"line_number":100,"context_line":"class SchemaValidationError(ValidationError):"}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_9ef17498","line":97,"updated":"2016-06-16 15:53:09.000000000","message":"The bit of text in the previous file is entirely static -- so it should live here.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"}],"keystone/identity/core.py":[{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"4ccf80dd9a8ed1c2242b558244fd07fc1945b453","unresolved":false,"context_lines":[{"line_number":849,"context_line":"    @exception_translated(\u0027user\u0027)"},{"line_number":850,"context_line":"    def create_user(self, user_ref, initiator\u003dNone):"},{"line_number":851,"context_line":"        user \u003d user_ref.copy()"},{"line_number":852,"context_line":"        validators.PasswordValidator().validate(user.get(\u0027password\u0027))"},{"line_number":853,"context_line":"        user[\u0027name\u0027] \u003d clean.user_name(user[\u0027name\u0027])"},{"line_number":854,"context_line":"        user.setdefault(\u0027enabled\u0027, True)"},{"line_number":855,"context_line":"        user[\u0027enabled\u0027] \u003d clean.user_enabled(user[\u0027enabled\u0027])"}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_7e8da8d2","line":852,"updated":"2016-06-16 15:53:09.000000000","message":"Can the validator support null values? The .get() will default to returning None if there\u0027s no password set.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c24899538df628ccdaadc7b4a65789ee7dcc36e3","unresolved":false,"context_lines":[{"line_number":849,"context_line":"    @exception_translated(\u0027user\u0027)"},{"line_number":850,"context_line":"    def create_user(self, user_ref, initiator\u003dNone):"},{"line_number":851,"context_line":"        user \u003d user_ref.copy()"},{"line_number":852,"context_line":"        validators.PasswordValidator().validate(user.get(\u0027password\u0027))"},{"line_number":853,"context_line":"        user[\u0027name\u0027] \u003d clean.user_name(user[\u0027name\u0027])"},{"line_number":854,"context_line":"        user.setdefault(\u0027enabled\u0027, True)"},{"line_number":855,"context_line":"        user[\u0027enabled\u0027] \u003d clean.user_enabled(user[\u0027enabled\u0027])"}],"source_content_type":"text/x-python","patch_set":37,"id":"5a9d85d2_cba08d93","line":852,"in_reply_to":"7aa08908_7e8da8d2","updated":"2016-06-20 16:08:12.000000000","message":"It should.  I\u0027ll create a test case for this.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"4ccf80dd9a8ed1c2242b558244fd07fc1945b453","unresolved":false,"context_lines":[{"line_number":933,"context_line":"    def update_user(self, user_id, user_ref, initiator\u003dNone):"},{"line_number":934,"context_line":"        old_user_ref \u003d self.get_user(user_id)"},{"line_number":935,"context_line":"        user \u003d user_ref.copy()"},{"line_number":936,"context_line":"        if user.get(\u0027password\u0027) is not None:"},{"line_number":937,"context_line":"            validators.PasswordValidator().validate(user.get(\u0027password\u0027))"},{"line_number":938,"context_line":"        if \u0027name\u0027 in user:"},{"line_number":939,"context_line":"            user[\u0027name\u0027] \u003d clean.user_name(user[\u0027name\u0027])"}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_9ee4d4ac","line":936,"updated":"2016-06-16 15:53:09.000000000","message":"This will allow \u0027password\u0027: null, to slip through unvalidated -- which means you\u0027ll be able to set a null password on update? Not sure that makes any sense?","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"49a8c891fc7a9ec6909b5317c1e31f137167b6d2","unresolved":false,"context_lines":[{"line_number":933,"context_line":"    def update_user(self, user_id, user_ref, initiator\u003dNone):"},{"line_number":934,"context_line":"        old_user_ref \u003d self.get_user(user_id)"},{"line_number":935,"context_line":"        user \u003d user_ref.copy()"},{"line_number":936,"context_line":"        if user.get(\u0027password\u0027) is not None:"},{"line_number":937,"context_line":"            validators.PasswordValidator().validate(user.get(\u0027password\u0027))"},{"line_number":938,"context_line":"        if \u0027name\u0027 in user:"},{"line_number":939,"context_line":"            user[\u0027name\u0027] \u003d clean.user_name(user[\u0027name\u0027])"}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_b1eaa10c","line":936,"in_reply_to":"7aa08908_9ee4d4ac","updated":"2016-06-16 15:57:36.000000000","message":"How is making sure a user must have a password enforced? Or is it not needed since that can\u0027t login without one?","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c24899538df628ccdaadc7b4a65789ee7dcc36e3","unresolved":false,"context_lines":[{"line_number":933,"context_line":"    def update_user(self, user_id, user_ref, initiator\u003dNone):"},{"line_number":934,"context_line":"        old_user_ref \u003d self.get_user(user_id)"},{"line_number":935,"context_line":"        user \u003d user_ref.copy()"},{"line_number":936,"context_line":"        if user.get(\u0027password\u0027) is not None:"},{"line_number":937,"context_line":"            validators.PasswordValidator().validate(user.get(\u0027password\u0027))"},{"line_number":938,"context_line":"        if \u0027name\u0027 in user:"},{"line_number":939,"context_line":"            user[\u0027name\u0027] \u003d clean.user_name(user[\u0027name\u0027])"}],"source_content_type":"text/x-python","patch_set":37,"id":"5a9d85d2_ab26e165","line":936,"in_reply_to":"7aa08908_9ee4d4ac","updated":"2016-06-20 16:08:12.000000000","message":"It does make sense.  Will fix this.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c24899538df628ccdaadc7b4a65789ee7dcc36e3","unresolved":false,"context_lines":[{"line_number":933,"context_line":"    def update_user(self, user_id, user_ref, initiator\u003dNone):"},{"line_number":934,"context_line":"        old_user_ref \u003d self.get_user(user_id)"},{"line_number":935,"context_line":"        user \u003d user_ref.copy()"},{"line_number":936,"context_line":"        if user.get(\u0027password\u0027) is not None:"},{"line_number":937,"context_line":"            validators.PasswordValidator().validate(user.get(\u0027password\u0027))"},{"line_number":938,"context_line":"        if \u0027name\u0027 in user:"},{"line_number":939,"context_line":"            user[\u0027name\u0027] \u003d clean.user_name(user[\u0027name\u0027])"}],"source_content_type":"text/x-python","patch_set":37,"id":"5a9d85d2_4b8dbd30","line":936,"in_reply_to":"7aa08908_b1eaa10c","updated":"2016-06-20 16:08:12.000000000","message":"If updating the password, the password will be part of the user dict. Otherwise not.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"4ccf80dd9a8ed1c2242b558244fd07fc1945b453","unresolved":false,"context_lines":[{"line_number":934,"context_line":"        old_user_ref \u003d self.get_user(user_id)"},{"line_number":935,"context_line":"        user \u003d user_ref.copy()"},{"line_number":936,"context_line":"        if user.get(\u0027password\u0027) is not None:"},{"line_number":937,"context_line":"            validators.PasswordValidator().validate(user.get(\u0027password\u0027))"},{"line_number":938,"context_line":"        if \u0027name\u0027 in user:"},{"line_number":939,"context_line":"            user[\u0027name\u0027] \u003d clean.user_name(user[\u0027name\u0027])"},{"line_number":940,"context_line":"        if \u0027enabled\u0027 in user:"}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_5e6c2c36","line":937,"updated":"2016-06-16 15:53:09.000000000","message":"You don\u0027t need to call .get() here if you\u0027ve already checked that there\u0027s a password.","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c24899538df628ccdaadc7b4a65789ee7dcc36e3","unresolved":false,"context_lines":[{"line_number":934,"context_line":"        old_user_ref \u003d self.get_user(user_id)"},{"line_number":935,"context_line":"        user \u003d user_ref.copy()"},{"line_number":936,"context_line":"        if user.get(\u0027password\u0027) is not None:"},{"line_number":937,"context_line":"            validators.PasswordValidator().validate(user.get(\u0027password\u0027))"},{"line_number":938,"context_line":"        if \u0027name\u0027 in user:"},{"line_number":939,"context_line":"            user[\u0027name\u0027] \u003d clean.user_name(user[\u0027name\u0027])"},{"line_number":940,"context_line":"        if \u0027enabled\u0027 in user:"}],"source_content_type":"text/x-python","patch_set":37,"id":"5a9d85d2_2b77913b","line":937,"in_reply_to":"7aa08908_5e6c2c36","updated":"2016-06-20 16:08:12.000000000","message":"Done","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"9d3db84f2e4a4c548963fa9e4fffdfa8172a3867","unresolved":false,"context_lines":[{"line_number":849,"context_line":"    @exception_translated(\u0027user\u0027)"},{"line_number":850,"context_line":"    def create_user(self, user_ref, initiator\u003dNone):"},{"line_number":851,"context_line":"        user \u003d user_ref.copy()"},{"line_number":852,"context_line":"        validators.PasswordValidator().validate(user.get(\u0027password\u0027))"},{"line_number":853,"context_line":"        user[\u0027name\u0027] \u003d clean.user_name(user[\u0027name\u0027])"},{"line_number":854,"context_line":"        user.setdefault(\u0027enabled\u0027, True)"},{"line_number":855,"context_line":"        user[\u0027enabled\u0027] \u003d clean.user_enabled(user[\u0027enabled\u0027])"}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_483efc8c","line":852,"updated":"2016-06-23 18:18:06.000000000","message":":)\n\n  if \u0027password\u0027 in user:\n    validators.validate_password(user[\u0027password\u0027])","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c46770b31e817774ed445a28492c2107bcd6065a","unresolved":false,"context_lines":[{"line_number":849,"context_line":"    @exception_translated(\u0027user\u0027)"},{"line_number":850,"context_line":"    def create_user(self, user_ref, initiator\u003dNone):"},{"line_number":851,"context_line":"        user \u003d user_ref.copy()"},{"line_number":852,"context_line":"        validators.PasswordValidator().validate(user.get(\u0027password\u0027))"},{"line_number":853,"context_line":"        user[\u0027name\u0027] \u003d clean.user_name(user[\u0027name\u0027])"},{"line_number":854,"context_line":"        user.setdefault(\u0027enabled\u0027, True)"},{"line_number":855,"context_line":"        user[\u0027enabled\u0027] \u003d clean.user_enabled(user[\u0027enabled\u0027])"}],"source_content_type":"text/x-python","patch_set":43,"id":"3aaa91ec_b1cd48b9","line":852,"in_reply_to":"3aaa91ec_483efc8c","updated":"2016-06-23 19:01:16.000000000","message":"Done","commit_id":"4b2ce8eb688d664a6ee22aceb7fecf9cda51b61d"}],"keystone/identity/schema.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"c412ffb6016bb3162c72ba8dd97046a3c574cc85","unresolved":false,"context_lines":[{"line_number":42,"context_line":"    }"},{"line_number":43,"context_line":"}"},{"line_number":44,"context_line":""},{"line_number":45,"context_line":"_password_properties \u003d {"},{"line_number":46,"context_line":"    \u0027password\u0027: {"},{"line_number":47,"context_line":"        \u0027type\u0027: [\u0027string\u0027, \u0027null\u0027],"},{"line_number":48,"context_line":"        \u0027password\u0027: _password_pattern"}],"source_content_type":"text/x-python","patch_set":20,"id":"9abb7d3a_a6ec4d90","line":45,"updated":"2016-06-03 00:59:11.000000000","message":"this might be re-usable on both line 40 - 41 and on line 68.","commit_id":"24a225fbd0f12d7b38095eae8ad0b1bdd0096548"}],"keystone/tests/unit/test_v3_identity.py":[{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"dbc98e46a9c0f5b5177527c3e91e545f470b6040","unresolved":false,"context_lines":[{"line_number":148,"context_line":"                  expected_status\u003dhttp_client.CREATED)"},{"line_number":149,"context_line":""},{"line_number":150,"context_line":"    def test_create_user_with_invalid_password(self):"},{"line_number":151,"context_line":"        CONF.security_compliance.password_regex \u003d ("},{"line_number":152,"context_line":"            \u0027^(?\u003d.*\\d)(?\u003d.*[a-zA-Z]).{7,}$\u0027)"},{"line_number":153,"context_line":"        user \u003d unit.new_user_ref(domain_id\u003dself.domain_id)"},{"line_number":154,"context_line":"        user[\u0027password\u0027] \u003d \u0027simple\u0027"}],"source_content_type":"text/x-python","patch_set":26,"id":"7aa08908_be89facc","line":151,"updated":"2016-06-06 12:37:53.000000000","message":"Use the config fixture to make sure the defaults are set back after the test executes.","commit_id":"4a61acfb0665df3b7f447ebbe45c85fc24981c4e"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"6d55634c8a9368dbc67b229f6b532f879bf0a0b7","unresolved":false,"context_lines":[{"line_number":148,"context_line":"                  expected_status\u003dhttp_client.CREATED)"},{"line_number":149,"context_line":""},{"line_number":150,"context_line":"    def test_create_user_with_invalid_password(self):"},{"line_number":151,"context_line":"        CONF.security_compliance.password_regex \u003d ("},{"line_number":152,"context_line":"            \u0027^(?\u003d.*\\d)(?\u003d.*[a-zA-Z]).{7,}$\u0027)"},{"line_number":153,"context_line":"        user \u003d unit.new_user_ref(domain_id\u003dself.domain_id)"},{"line_number":154,"context_line":"        user[\u0027password\u0027] \u003d \u0027simple\u0027"}],"source_content_type":"text/x-python","patch_set":26,"id":"7aa08908_6ce877e3","line":151,"in_reply_to":"7aa08908_be89facc","updated":"2016-06-06 16:52:35.000000000","message":"Done","commit_id":"4a61acfb0665df3b7f447ebbe45c85fc24981c4e"},{"author":{"_account_id":6460,"name":"Brad Topol","email":"btopol@us.ibm.com","username":"btopol"},"change_message_id":"9366640b9d330a6be2c74dd79a9e669598eb915d","unresolved":false,"context_lines":[{"line_number":404,"context_line":"        user \u003d unit.create_user(self.identity_api,"},{"line_number":405,"context_line":"                                domain_id\u003dself.domain[\u0027id\u0027])"},{"line_number":406,"context_line":"        # set password strength and update password"},{"line_number":407,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"},{"line_number":408,"context_line":"                                   password_regex\u003d("},{"line_number":409,"context_line":"                                       \u0027^(?\u003d.*\\d)(?\u003d.*[a-zA-Z]).{7,}$\u0027))"},{"line_number":410,"context_line":"        user[\u0027password\u0027] \u003d \u0027simple\u0027"}],"source_content_type":"text/x-python","patch_set":30,"id":"7aa08908_cceaf2ab","line":407,"updated":"2016-06-07 23:07:14.000000000","message":"should have a comment that says # password requires: 1 letter, 1 digit, 7 chars","commit_id":"1049bc2c2befbf74ab83d0c1808ebe8245536e3f"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"07c19062899833fd0798ff118dcc55865eccda78","unresolved":false,"context_lines":[{"line_number":404,"context_line":"        user \u003d unit.create_user(self.identity_api,"},{"line_number":405,"context_line":"                                domain_id\u003dself.domain[\u0027id\u0027])"},{"line_number":406,"context_line":"        # set password strength and update password"},{"line_number":407,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"},{"line_number":408,"context_line":"                                   password_regex\u003d("},{"line_number":409,"context_line":"                                       \u0027^(?\u003d.*\\d)(?\u003d.*[a-zA-Z]).{7,}$\u0027))"},{"line_number":410,"context_line":"        user[\u0027password\u0027] \u003d \u0027simple\u0027"}],"source_content_type":"text/x-python","patch_set":30,"id":"7aa08908_cc7032df","line":407,"in_reply_to":"7aa08908_cceaf2ab","updated":"2016-06-07 23:17:30.000000000","message":"Done","commit_id":"1049bc2c2befbf74ab83d0c1808ebe8245536e3f"},{"author":{"_account_id":6460,"name":"Brad Topol","email":"btopol@us.ibm.com","username":"btopol"},"change_message_id":"9366640b9d330a6be2c74dd79a9e669598eb915d","unresolved":false,"context_lines":[{"line_number":854,"context_line":"        self.assertNotIn(new_password, log_fix.output)"},{"line_number":855,"context_line":""},{"line_number":856,"context_line":"    def test_changing_password_with_simple_password_strength(self):"},{"line_number":857,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"},{"line_number":858,"context_line":"                                   password_regex\u003d\u0027[\\S]+\u0027)"},{"line_number":859,"context_line":"        self.change_password(password\u003d\u0027simple\u0027,"},{"line_number":860,"context_line":"                             original_password\u003dself.user_ref[\u0027password\u0027],"}],"source_content_type":"text/x-python","patch_set":30,"id":"7aa08908_8c8f4a29","line":857,"updated":"2016-06-07 23:07:14.000000000","message":"should have comment that says # password requires: any non-whitespace character","commit_id":"1049bc2c2befbf74ab83d0c1808ebe8245536e3f"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"07c19062899833fd0798ff118dcc55865eccda78","unresolved":false,"context_lines":[{"line_number":854,"context_line":"        self.assertNotIn(new_password, log_fix.output)"},{"line_number":855,"context_line":""},{"line_number":856,"context_line":"    def test_changing_password_with_simple_password_strength(self):"},{"line_number":857,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"},{"line_number":858,"context_line":"                                   password_regex\u003d\u0027[\\S]+\u0027)"},{"line_number":859,"context_line":"        self.change_password(password\u003d\u0027simple\u0027,"},{"line_number":860,"context_line":"                             original_password\u003dself.user_ref[\u0027password\u0027],"}],"source_content_type":"text/x-python","patch_set":30,"id":"7aa08908_2c7a96bd","line":857,"in_reply_to":"7aa08908_8c8f4a29","updated":"2016-06-07 23:17:30.000000000","message":"Done","commit_id":"1049bc2c2befbf74ab83d0c1808ebe8245536e3f"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"3fb4ccca8bb277c18871e1d6bf46a99e4d866000","unresolved":false,"context_lines":[{"line_number":147,"context_line":"        self.post(\u0027/users\u0027, body\u003d{\u0027user\u0027: ref}, token\u003dself.get_admin_token(),"},{"line_number":148,"context_line":"                  expected_status\u003dhttp_client.CREATED)"},{"line_number":149,"context_line":""},{"line_number":150,"context_line":"    def test_create_user_with_invalid_password(self):"},{"line_number":151,"context_line":"        # password requires: 1 letter, 1 digit, 7 chars"},{"line_number":152,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"},{"line_number":153,"context_line":"                                   password_regex\u003d("}],"source_content_type":"text/x-python","patch_set":34,"id":"7aa08908_09b8fae4","line":150,"updated":"2016-06-09 13:28:45.000000000","message":"I think all of the password tests deserve their own class so the config fixture can just be used in the setUp.","commit_id":"fe1eb8a43d7d9d0c132fdde04d0f9456dfca867a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"2b04023f4be432364657f86cdb88a6b2146c25fa","unresolved":false,"context_lines":[{"line_number":147,"context_line":"        self.post(\u0027/users\u0027, body\u003d{\u0027user\u0027: ref}, token\u003dself.get_admin_token(),"},{"line_number":148,"context_line":"                  expected_status\u003dhttp_client.CREATED)"},{"line_number":149,"context_line":""},{"line_number":150,"context_line":"    def test_create_user_with_invalid_password(self):"},{"line_number":151,"context_line":"        # password requires: 1 letter, 1 digit, 7 chars"},{"line_number":152,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"},{"line_number":153,"context_line":"                                   password_regex\u003d("}],"source_content_type":"text/x-python","patch_set":34,"id":"7aa08908_c7d7e0bd","line":150,"in_reply_to":"7aa08908_09b8fae4","updated":"2016-06-09 20:43:37.000000000","message":"Done","commit_id":"fe1eb8a43d7d9d0c132fdde04d0f9456dfca867a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"baae4a663ac528f58f5319689b1cf16ae36d7a0f","unresolved":false,"context_lines":[{"line_number":147,"context_line":"        self.post(\u0027/users\u0027, body\u003d{\u0027user\u0027: ref}, token\u003dself.get_admin_token(),"},{"line_number":148,"context_line":"                  expected_status\u003dhttp_client.CREATED)"},{"line_number":149,"context_line":""},{"line_number":150,"context_line":"    def test_create_user_with_invalid_password(self):"},{"line_number":151,"context_line":"        # password requires: 1 letter, 1 digit, 7 chars"},{"line_number":152,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"},{"line_number":153,"context_line":"                                   password_regex\u003d("}],"source_content_type":"text/x-python","patch_set":34,"id":"7aa08908_5fcd915c","line":150,"in_reply_to":"7aa08908_09b8fae4","updated":"2016-06-09 14:29:59.000000000","message":"Good suggestion, thanks.  I\u0027ll get this in the next patch.","commit_id":"fe1eb8a43d7d9d0c132fdde04d0f9456dfca867a"},{"author":{"_account_id":4,"name":"Dolph Mathews","email":"dolph.mathews@gmail.com","username":"dolph"},"change_message_id":"4ccf80dd9a8ed1c2242b558244fd07fc1945b453","unresolved":false,"context_lines":[{"line_number":791,"context_line":"        self.assertNotIn(new_password, log_fix.output)"},{"line_number":792,"context_line":""},{"line_number":793,"context_line":""},{"line_number":794,"context_line":"class PasswordValidationTestCase(UserSelfServiceChangingPasswordsTestCase):"},{"line_number":795,"context_line":"    \"\"\"Test password validation.\"\"\""},{"line_number":796,"context_line":""},{"line_number":797,"context_line":"    def setUp(self):"}],"source_content_type":"text/x-python","patch_set":37,"id":"7aa08908_7e52c836","line":794,"updated":"2016-06-16 15:53:09.000000000","message":"I\u0027d like to see additional negative tests here, like passing in null values, other data types, and empty strings.\n\n  password\u003dNone\n  password\u003d1234 (numeric)\n  password\u003dTrue (boolean)","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"8faec6621aeb17c597f1cb75fa20be4724b4245e","unresolved":false,"context_lines":[{"line_number":791,"context_line":"        self.assertNotIn(new_password, log_fix.output)"},{"line_number":792,"context_line":""},{"line_number":793,"context_line":""},{"line_number":794,"context_line":"class PasswordValidationTestCase(UserSelfServiceChangingPasswordsTestCase):"},{"line_number":795,"context_line":"    \"\"\"Test password validation.\"\"\""},{"line_number":796,"context_line":""},{"line_number":797,"context_line":"    def setUp(self):"}],"source_content_type":"text/x-python","patch_set":37,"id":"5a9d85d2_50460924","line":794,"in_reply_to":"7aa08908_7e52c836","updated":"2016-06-20 16:08:29.000000000","message":"Done","commit_id":"72718fe8853924c8b5d461d3047db711641f9f4a"}],"keystone/tests/unit/test_validation.py":[{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"ef0248e296e55433b9e09702ca34f3dd67efa3a7","unresolved":false,"context_lines":[{"line_number":2184,"context_line":""},{"line_number":2185,"context_line":"    def test_password_validate_with_valid_strong_password(self):"},{"line_number":2186,"context_line":"        password \u003d \u0027mypassword2\u0027"},{"line_number":2187,"context_line":"        self.assertIsNone(validators.validate_password(password))"},{"line_number":2188,"context_line":""},{"line_number":2189,"context_line":"    def test_password_validate_with_invalid_strong_password(self):"},{"line_number":2190,"context_line":"        # negative test: None"}],"source_content_type":"text/x-python","patch_set":47,"id":"3aaa91ec_d70d64e9","line":2187,"updated":"2016-06-23 19:47:28.000000000","message":"Interesting, I\u0027ve never seen self.assertIsNone used like this before for the validation tests. Typically, we just make the call because if a validation error is raised it will be captured in the test output and fail.\n\n    password \u003d \u0027mypassword2\u0027\n    self.validator.validate(password)\n\nThe absence of wrapping the validation in an assertRaises() essentially confirms that the call to validate the password did what we expected it to.","commit_id":"c4f2f44e6b1470a30139305e6426b9d2b9d73875"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"c91c9ff2ce3327b80f3c1e9473b8f6ef636cfffb","unresolved":false,"context_lines":[{"line_number":2184,"context_line":""},{"line_number":2185,"context_line":"    def test_password_validate_with_valid_strong_password(self):"},{"line_number":2186,"context_line":"        password \u003d \u0027mypassword2\u0027"},{"line_number":2187,"context_line":"        self.assertIsNone(validators.validate_password(password))"},{"line_number":2188,"context_line":""},{"line_number":2189,"context_line":"    def test_password_validate_with_invalid_strong_password(self):"},{"line_number":2190,"context_line":"        # negative test: None"}],"source_content_type":"text/x-python","patch_set":47,"id":"3aaa91ec_a25a1828","line":2187,"in_reply_to":"3aaa91ec_d70d64e9","updated":"2016-06-23 20:00:37.000000000","message":"Done","commit_id":"c4f2f44e6b1470a30139305e6426b9d2b9d73875"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8071e8558b05214053830b530f7cafad1d1161d9","unresolved":false,"context_lines":[{"line_number":2204,"context_line":"                          password)"},{"line_number":2205,"context_line":""},{"line_number":2206,"context_line":"    def test_password_validate_with_invalid_password_regex(self):"},{"line_number":2207,"context_line":"        # invalid regular expression, missing beginngin \u0027[\u0027"},{"line_number":2208,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"},{"line_number":2209,"context_line":"                                   password_regex\u003d\u0027\\S]+\u0027)"},{"line_number":2210,"context_line":"        password \u003d \u0027mypassword2\u0027"}],"source_content_type":"text/x-python","patch_set":60,"id":"1aa78d24_1a06cecd","line":2207,"updated":"2016-07-12 15:21:09.000000000","message":"beginning*","commit_id":"63e98197550f89916c93cad27a5d741dd694489c"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"5aaad192e828a3c769c8de70f859e28d62740043","unresolved":false,"context_lines":[{"line_number":2204,"context_line":"                          password)"},{"line_number":2205,"context_line":""},{"line_number":2206,"context_line":"    def test_password_validate_with_invalid_password_regex(self):"},{"line_number":2207,"context_line":"        # invalid regular expression, missing beginngin \u0027[\u0027"},{"line_number":2208,"context_line":"        self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"},{"line_number":2209,"context_line":"                                   password_regex\u003d\u0027\\S]+\u0027)"},{"line_number":2210,"context_line":"        password \u003d \u0027mypassword2\u0027"}],"source_content_type":"text/x-python","patch_set":60,"id":"1aa78d24_36ac7e8b","line":2207,"in_reply_to":"1aa78d24_1a06cecd","updated":"2016-07-12 17:46:48.000000000","message":"Done","commit_id":"63e98197550f89916c93cad27a5d741dd694489c"}]}
