This also fixes the handling of single-character names, which were mistakenly forbidden before. --- pyanaconda/regexes.py | 7 +++++-- tests/regex_tests/username_test.py | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/pyanaconda/regexes.py b/pyanaconda/regexes.py index 44acc0b..5859e59 100644 --- a/pyanaconda/regexes.py +++ b/pyanaconda/regexes.py @@ -38,10 +38,13 @@ GECOS_VALID = re.compile(r'^[^:]*$') # and UT_NAMESIZE for user names (which is defined as 32 bits/utmp.h). This # expression captures all of that: the initial character, followed by either # up to 30 portable characters and a dollar sign or up to 31 portable characters, -# both for a maximum total of 32. The empty string is not allowed. +# both for a maximum total of 32. The empty string is not allowed. "root" is not +# allowed.
# a base expression without anchors, helpful for building other expressions -_USERNAME_BASE = r'[a-zA-Z0-9._](([a-zA-Z0-9._-]{,30}$)|([a-zA-Z0-9._-]{,31}))' +# If the string is the right length to match "root", use a lookback expression +# to make sure it isn't. +_USERNAME_BASE = r'[a-zA-Z0-9._](([a-zA-Z0-9._-]{1,2})|([a-zA-Z0-9._-]{3}(?<!root))|([a-zA-Z0-9._-]{,30}$)|([a-zA-Z0-9._-]{4,31}))?'
USERNAME_VALID = re.compile(r'^' + _USERNAME_BASE + '$') GROUPNAME_VALID = USERNAME_VALID diff --git a/tests/regex_tests/username_test.py b/tests/regex_tests/username_test.py index 9877410..0c0d5d2 100644 --- a/tests/regex_tests/username_test.py +++ b/tests/regex_tests/username_test.py @@ -29,14 +29,14 @@ class UsernameRegexTestCase(unittest.TestCase): for good in goodlist: try: self.assertIsNotNone(expression.match(good)) - except AssertionError as error: + except AssertionError: got_error = True print("Good string %s did not match expression" % good)
for bad in badlist: try: self.assertIsNone(expression.match(bad)) - except AssertionError as error: + except AssertionError: got_error = True print("Bad string %s matched expression" % bad)
@@ -70,7 +70,12 @@ class UsernameRegexTestCase(unittest.TestCase): 'g_burdell', '_burdell', 'gggggggggggggggggggggggggburdell', # 32 characters - 'ggggggggggggggggggggggggburdell$' + 'ggggggggggggggggggggggggburdell$', + '_', + 'r', + 'ro', + 'roo', + 'roota', ]
bad_tests = [ @@ -83,7 +88,10 @@ class UsernameRegexTestCase(unittest.TestCase): 'ggggggggggggggggggggggggggburdell', # 33 characters 'gggggggggggggggggggggggggburdell$', ' gburdell', - ':gburdell' + ':gburdell', + 'root', + '$', + '-' ]
self._run_tests(USERNAME_VALID, good_tests, bad_tests)
On Wed, 2013-09-18 at 16:15 -0400, David Shea wrote:
This also fixes the handling of single-character names, which were mistakenly forbidden before.
pyanaconda/regexes.py | 7 +++++-- tests/regex_tests/username_test.py | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/pyanaconda/regexes.py b/pyanaconda/regexes.py index 44acc0b..5859e59 100644 --- a/pyanaconda/regexes.py +++ b/pyanaconda/regexes.py @@ -38,10 +38,13 @@ GECOS_VALID = re.compile(r'^[^:]*$') # and UT_NAMESIZE for user names (which is defined as 32 bits/utmp.h). This # expression captures all of that: the initial character, followed by either # up to 30 portable characters and a dollar sign or up to 31 portable characters, -# both for a maximum total of 32. The empty string is not allowed. +# both for a maximum total of 32. The empty string is not allowed. "root" is not +# allowed.
# a base expression without anchors, helpful for building other expressions -_USERNAME_BASE = r'[a-zA-Z0-9._](([a-zA-Z0-9._-]{,30}$)|([a-zA-Z0-9._-]{,31}))' +# If the string is the right length to match "root", use a lookback expression +# to make sure it isn't. +_USERNAME_BASE = r'[a-zA-Z0-9._](([a-zA-Z0-9._-]{1,2})|([a-zA-Z0-9._-]{3}(?<!root))|([a-zA-Z0-9._-]{,30}$)|([a-zA-Z0-9._-]{4,31}))?'
USERNAME_VALID = re.compile(r'^' + _USERNAME_BASE + '$') GROUPNAME_VALID = USERNAME_VALID diff --git a/tests/regex_tests/username_test.py b/tests/regex_tests/username_test.py index 9877410..0c0d5d2 100644 --- a/tests/regex_tests/username_test.py +++ b/tests/regex_tests/username_test.py @@ -29,14 +29,14 @@ class UsernameRegexTestCase(unittest.TestCase): for good in goodlist: try: self.assertIsNotNone(expression.match(good))
except AssertionError as error:
except AssertionError: got_error = True print("Good string %s did not match expression" % good) for bad in badlist: try: self.assertIsNone(expression.match(bad))
except AssertionError as error:
except AssertionError: got_error = True print("Bad string %s matched expression" % bad)@@ -70,7 +70,12 @@ class UsernameRegexTestCase(unittest.TestCase): 'g_burdell', '_burdell', 'gggggggggggggggggggggggggburdell', # 32 characters
'ggggggggggggggggggggggggburdell$'
'ggggggggggggggggggggggggburdell$','_','r','ro','roo','roota', ] bad_tests = [@@ -83,7 +88,10 @@ class UsernameRegexTestCase(unittest.TestCase): 'ggggggggggggggggggggggggggburdell', # 33 characters 'gggggggggggggggggggggggggburdell$', ' gburdell',
':gburdell'
':gburdell','root','$','-' ] self._run_tests(USERNAME_VALID, good_tests, bad_tests)
ACK.
On 09/18/2013 04:15 PM, David Shea wrote:
+_USERNAME_BASE = r'[a-zA-Z0-9._](([a-zA-Z0-9._-]{1,2})|([a-zA-Z0-9._-]{3}(?<!root))|([a-zA-Z0-9._-]{,30}$)|([a-zA-Z0-9._-]{4,31}))?'
So, looking at this thing, I think it'd be slightly better to rearrange the choices in the part of the expression after the first character.
_USERNAME_BASE = r'[a-zA-Z0-9._](([a-zA-Z0-9._-]{0,2})|([a-zA-Z0-9._-]{3}(?<!root))|([a-zA-Z0-9._-]{4,31})|([a-zA-Z0-9._-]{,30}$))'
That way it reads as an expression matching 0-2 characters, an expression matching 3 characters that isn't "root", an expression matching 4-31 characters, or an expression matching 0-30 characters followed by a dollar sign. It reads a little better (lol), and having the empty string matched by the first part means we don't need the question mark at the end.
On Fri, Sep 20, 2013 at 09:13:06AM -0400, David Shea wrote:
On 09/18/2013 04:15 PM, David Shea wrote:
+_USERNAME_BASE = r'[a-zA-Z0-9._](([a-zA-Z0-9._-]{1,2})|([a-zA-Z0-9._-]{3}(?<!root))|([a-zA-Z0-9._-]{,30}$)|([a-zA-Z0-9._-]{4,31}))?'
So, looking at this thing, I think it'd be slightly better to rearrange the choices in the part of the expression after the first character.
_USERNAME_BASE = r'[a-zA-Z0-9._](([a-zA-Z0-9._-]{0,2})|([a-zA-Z0-9._-]{3}(?<!root))|([a-zA-Z0-9._-]{4,31})|([a-zA-Z0-9._-]{,30}$))'
That way it reads as an expression matching 0-2 characters, an expression matching 3 characters that isn't "root", an expression matching 4-31 characters, or an expression matching 0-30 characters followed by a dollar sign. It reads a little better (lol), and having the empty string matched by the first part means we don't need the question mark at the end.
I guess if that works... But I'd be more inclined to just add a separate check wherever we use it.
anaconda-patches@lists.fedorahosted.org