diff options
author | Philipp Hörist <philipp@hoerist.com> | 2023-02-05 17:23:34 +0300 |
---|---|---|
committer | Philipp Hörist <philipp@hoerist.com> | 2023-02-05 17:54:38 +0300 |
commit | 83f0dd1de8ef8a5c2ce1a087c4e7e38856b5cb1c (patch) | |
tree | 26f2eb6f6719f4c684dfb0d2dbf8d21a5e979c12 | |
parent | 8883336f27cff82dc847acf1fc2f266a69475b1c (diff) |
imprv: Add SASLprep profile
-rw-r--r-- | nbxmpp/stringprep.py | 48 | ||||
-rw-r--r-- | test/unit/test_stringprep.py | 54 |
2 files changed, 102 insertions, 0 deletions
diff --git a/nbxmpp/stringprep.py b/nbxmpp/stringprep.py index 84943e4..9f3a43e 100644 --- a/nbxmpp/stringprep.py +++ b/nbxmpp/stringprep.py @@ -241,3 +241,51 @@ def nameprep(string: str, allow_unassigned: bool = False) -> str: ) return ''.join(chars) + + +def _saslprep_do_mapping(chars: list[str]) -> None: + i = 0 + while i < len(chars): + c = chars[i] + if stringprep.in_table_b1(c): + del chars[i] + + elif stringprep.in_table_c12(c): + chars[i] = ' ' + + i += 1 + + +def saslprep(string: str, allow_unassigned: bool = False) -> str: + ''' + Process the given `string` using the SASLprep (`RFC 4013`_) profile. + ''' + + chars = list(string) + _saslprep_do_mapping(chars) + do_normalization(chars) + check_against_tables( + chars, + ( + stringprep.in_table_c12, + stringprep.in_table_c21, + stringprep.in_table_c22, + stringprep.in_table_c3, + stringprep.in_table_c4, + stringprep.in_table_c5, + stringprep.in_table_c6, + stringprep.in_table_c7, + stringprep.in_table_c8, + stringprep.in_table_c9, + )) + check_bidi(chars) + + if not allow_unassigned: + check_against_tables( + chars, + ( + stringprep.in_table_a1, + ) + ) + + return ''.join(chars) diff --git a/test/unit/test_stringprep.py b/test/unit/test_stringprep.py index 06cb769..5817a29 100644 --- a/test/unit/test_stringprep.py +++ b/test/unit/test_stringprep.py @@ -1,6 +1,7 @@ import unittest from nbxmpp.stringprep import nodeprep +from nbxmpp.stringprep import saslprep from nbxmpp.stringprep import resourceprep from nbxmpp.stringprep import nameprep from nbxmpp.stringprep import check_bidi @@ -201,3 +202,56 @@ class TestResourceprep(unittest.TestCase): self.assertEqual( '\u0221', resourceprep('\u0221', allow_unassigned=True)) + + +class TestSASLprep(unittest.TestCase): + def test_map_to_nothing(self): + self.assertEqual( + 'IX', + saslprep('I\u00ADX'), + 'SASLprep requirement: map SOFT HYPHEN to nothing') + + def test_nfkc(self): + self.assertEqual( + 'a', + saslprep('\u00AA'), + 'SASLprep requirement: NFKC') + self.assertEqual( + 'IX', + saslprep('\u2168'), + 'SASLprep requirement: NFKC') + + def test_case_fold(self): + self.assertNotEqual( + 'user', + saslprep('USER'), + 'SASLprep requirement: No CaseFold') + + def test_prohibited_character(self): + with self.assertRaisesRegex( + ValueError, + r'U\+0007', + msg='SASLprep requirement: ' + 'prohibited character (C.2.1)'): + saslprep('\u0007') + + def test_bidi_check(self): + with self.assertRaises( + ValueError, + msg='SASLprep requirement: bidi check'): + saslprep('\u0627\u0031') + + def test_unassigned(self): + with self.assertRaises( + ValueError, + msg='SASLprep requirement: unassigned'): + saslprep('\u0221', allow_unassigned=False) + + with self.assertRaises( + ValueError, + msg='enforce no unassigned by default'): + saslprep('\u0221') + + self.assertEqual( + '\u0221', + saslprep('\u0221', allow_unassigned=True)) |