Lines 42-54
from ucsschool.importer.utils.logging import get_logger
|
Link Here
|
---|
|
42 |
|
42 |
|
43 |
|
43 |
|
44 |
class UsernameHandler(object): |
44 |
class UsernameHandler(object): |
45 |
replacement_variable_pattern = re.compile(r"\[.*?\]") |
45 |
""" |
|
|
46 |
>>> BAD_CHARS = ''.join(set(map(chr, range(128))) - set('.1032547698ACBEDGFIHKJMLONQPSRUTWVYXZacbedgfihkjmlonqpsrutwvyxz')) |
47 |
>>> UsernameHandler(20).format_username('Max.Mustermann') |
48 |
'Max.Mustermann' |
49 |
>>> UsernameHandler(20).format_username('Foo[COUNTER2][COUNTER2]') # doctest: +IGNORE_EXCEPTION_DETAIL |
50 |
Traceback (most recent call last): |
51 |
... |
52 |
FormatError: |
53 |
>>> UsernameHandler(20).format_username('.') # doctest: +IGNORE_EXCEPTION_DETAIL |
54 |
Traceback (most recent call last): |
55 |
... |
56 |
FormatError: |
57 |
>>> UsernameHandler(20).format_username('.Max.Mustermann.') |
58 |
'Max.Mustermann' |
59 |
>>> UsernameHandler(4).format_username('Max.Mustermann') |
60 |
'Max' |
61 |
>>> for c in BAD_CHARS: |
62 |
... assert 'Max' == UsernameHandler(20).format_username('Ma%sx' % (c,)) |
63 |
... |
64 |
>>> UsernameHandler(20).format_username('Max.Mustermann12.4567890') |
65 |
'Max.Mustermann12.456' |
66 |
>>> for c in '.1032547698ACBEDGFIHKJMLONQPSRUTWVYXZacbedgfihkjmlonqpsrutwvyxz': |
67 |
... assert 'Ma%sx' % (c,) == UsernameHandler(20).format_username('Ma%sx' % (c,)) |
68 |
... |
69 |
>>> UsernameHandler(20).format_username('Max[Muster]Mann') |
70 |
'MaxMusterMann' |
71 |
>>> UsernameHandler(20).format_username('Max[ALWAYSCOUNTER].Mustermann') |
72 |
'Max1.Mustermann' |
73 |
>>> UsernameHandler(20).format_username('Max[ALWAYSCOUNTER].Mustermann') |
74 |
'Max2.Mustermann' |
75 |
>>> UsernameHandler(20).format_username('Max[ALWAYSCOUNTER].Mustermann') |
76 |
'Max3.Mustermann' |
77 |
>>> UsernameHandler(20).format_username('Max[COUNTER2].Mustermann') |
78 |
'Max4.Mustermann' |
79 |
>>> UsernameHandler(20).format_username('Maria[ALWAYSCOUNTER].Musterfrau') |
80 |
'Maria1.Musterfrau' |
81 |
>>> UsernameHandler(20).format_username('Moritz[COUNTER2]') |
82 |
'Moritz' |
83 |
>>> UsernameHandler(20).format_username('Moritz[COUNTER2]') |
84 |
'Moritz2' |
85 |
>>> for i, c in enumerate(BAD_CHARS, 1): |
86 |
... assert 'Foo%d' % (i) == UsernameHandler(20).format_username('Fo%so[ALWAYSCOUNTER]' % (c,)) |
87 |
>>> UsernameHandler(8).format_username('aaaa[COUNTER2]bbbbcccc') |
88 |
'aaaab' |
89 |
>>> UsernameHandler(8).format_username('aaaa[COUNTER2]bbbbcccc') |
90 |
'aaaa2b' |
91 |
>>> UsernameHandler(8).format_username('bbbb[ALWAYSCOUNTER]ccccdddd') |
92 |
'bbbb1c' |
93 |
>>> UsernameHandler(8).format_username('bbbb[ALWAYSCOUNTER]ccccdddd') |
94 |
'bbbb2c' |
95 |
""" |
96 |
|
46 |
allowed_chars = string.ascii_letters + string.digits + "." |
97 |
allowed_chars = string.ascii_letters + string.digits + "." |
47 |
|
98 |
|
48 |
def __init__(self, username_max_length): |
99 |
def __init__(self, username_max_length): |
49 |
self.username_max_length = username_max_length |
100 |
self.username_max_length = username_max_length |
50 |
self.logger = get_logger() |
101 |
self.logger = get_logger() |
51 |
self.connection, self.position = get_admin_connection() |
102 |
self.connection, self.position = get_admin_connection() |
|
|
103 |
self.replacement_variable_pattern = re.compile(r'(%s)' % '|'.join(map(re.escape, self.counter_variable_to_function.keys())), flags=re.I) |
52 |
|
104 |
|
53 |
def add_to_ldap(self, username, first_number): |
105 |
def add_to_ldap(self, username, first_number): |
54 |
assert isinstance(username, basestring) |
106 |
assert isinstance(username, basestring) |
Lines 92-100
class UsernameHandler(object):
|
Link Here
|
---|
|
92 |
:param name: str: username to check |
144 |
:param name: str: username to check |
93 |
:return: str: copy of input, possibly modified |
145 |
:return: str: copy of input, possibly modified |
94 |
""" |
146 |
""" |
95 |
bad_chars = "".join(sorted(set(name).difference(set(self.allowed_chars)))) |
147 |
bad_chars = ''.join(set(name).difference(set(self.allowed_chars))) |
96 |
if bad_chars: |
148 |
if bad_chars: |
97 |
self.logger.warn("Removing disallowed characters %r from username %r.", bad_chars, name) |
149 |
self.logger.warn("Removing disallowed characters %r from username %r.", ''.join(sorted(bad_chars)), name) |
98 |
if name.startswith(".") or name.endswith("."): |
150 |
if name.startswith(".") or name.endswith("."): |
99 |
self.logger.warn("Removing disallowed dot from start and end of username %r.", name) |
151 |
self.logger.warn("Removing disallowed dot from start and end of username %r.", name) |
100 |
name = name.strip(".") |
152 |
name = name.strip(".") |
Lines 116-177
class UsernameHandler(object):
|
Link Here
|
---|
|
116 |
:return: str: unique username |
168 |
:return: str: unique username |
117 |
""" |
169 |
""" |
118 |
assert isinstance(name, basestring) |
170 |
assert isinstance(name, basestring) |
119 |
ori_name = name |
171 |
PATTERN_FUNC_MAXLENGTH = 3 # maximum a counter function can produce is len('999') |
120 |
cut_pos = self.username_max_length - 3 # numbers >999 are not supported |
172 |
username = name |
121 |
|
173 |
|
122 |
match = self.replacement_variable_pattern.search(name) |
174 |
match = self.replacement_variable_pattern.search(name) |
123 |
if not match: |
175 |
if match: |
124 |
# no counter variable used, just check characters and length |
176 |
func = self.counter_variable_to_function[match.group().upper()] |
125 |
name = self.remove_bad_chars(name) |
177 |
cut_pos = self.username_max_length - PATTERN_FUNC_MAXLENGTH |
126 |
if len(name) > self.username_max_length: |
178 |
|
127 |
res = name[:self.username_max_length] |
179 |
# it's not allowed to have two [COUNTER] patterns |
128 |
self.logger.warn("Username %r too long, shortened to %r.", name, res) |
180 |
if len(self.replacement_variable_pattern.findall(name)) >= 2: |
129 |
else: |
181 |
raise FormatError("More than one counter variable found in username scheme '{}'.".format(name), name, name) |
130 |
res = name |
182 |
|
131 |
return res |
183 |
# the variable must no be the [COUNTER] pattern |
132 |
|
184 |
without_pattern = self.replacement_variable_pattern.sub('', name) |
133 |
if len(self.replacement_variable_pattern.split(name)) > 2: |
185 |
|
134 |
raise FormatError("More than one counter variable found in username scheme '{}'.".format(name), name, name) |
186 |
username = name |
135 |
|
187 |
if len(without_pattern) > cut_pos: |
136 |
# need username without counter variable to calculate length |
188 |
without_pattern = self.remove_bad_chars(without_pattern)[:cut_pos] |
137 |
_base_name = "".join(self.replacement_variable_pattern.split(name)) |
189 |
start, end = without_pattern[:match.start()], without_pattern[match.start():] |
138 |
base_name = self.remove_bad_chars(_base_name) |
190 |
username = '%s[%s]%s' % (start, match.group(), end) |
139 |
if _base_name != base_name: |
191 |
username = self.replacement_variable_pattern.sub(func(without_pattern), username) |
140 |
# recalculate position of pattern |
192 |
|
141 |
name = "{}{}{}".format(base_name[:match.start()], match.group(), base_name[match.end():]) |
193 |
username = self.remove_bad_chars(username) |
142 |
match = self.replacement_variable_pattern.search(name) |
194 |
|
143 |
|
195 |
if not match and len(name) > self.username_max_length: |
144 |
variable = match.group() |
196 |
username = username[:self.username_max_length] |
145 |
start = match.start() |
197 |
self.logger.warn("Username %r too long, shortened to %r.", name, username) |
146 |
end = match.end() |
198 |
|
147 |
|
199 |
username = username.strip('.') |
148 |
if start == 0 and end == len(name): |
200 |
if not username: |
149 |
raise FormatError("No username in '{}'.".format(name), ori_name, ori_name) |
201 |
raise FormatError("No username in '{}'.".format(name), name, name) |
150 |
|
202 |
return username |
151 |
# get counter function |
|
|
152 |
try: |
153 |
func = self.counter_variable_to_function[variable.upper()] |
154 |
except KeyError as exc: |
155 |
raise FormatError("Unknown variable name '{}' in username scheme '{}': '{}' not in known variables: '{}'".format( |
156 |
variable, ori_name, exc, self.counter_variable_to_function.keys()), variable, name) |
157 |
except AttributeError as exc: |
158 |
raise FormatError("No method '{}' can be found for variable name '{}' in username scheme '{}': {}".format( |
159 |
self.counter_variable_to_function[variable], variable, name, exc), variable, ori_name) |
160 |
|
161 |
if len(base_name) > cut_pos: |
162 |
# base name without variable to long, we have to shorten it |
163 |
# numbers will only be appended, no inserting possible anymore |
164 |
res = base_name[:cut_pos] |
165 |
insert_position = cut_pos |
166 |
self.logger.warn("Username %r too long, shortened to %r.", base_name, res) |
167 |
res = self.remove_bad_chars(res) # dot from middle might be at end now |
168 |
else: |
169 |
insert_position = start |
170 |
res = u"{}{}".format(name[:start], name[end:]) |
171 |
|
172 |
counter = func(res) # get counter number to insert/append |
173 |
ret = "{}{}{}".format(res[:insert_position], counter, res[insert_position:]) |
174 |
return ret |
175 |
|
203 |
|
176 |
@property |
204 |
@property |
177 |
def counter_variable_to_function(self): |
205 |
def counter_variable_to_function(self): |
Lines 217-219
class UsernameHandler(object):
|
Link Here
|
---|
|
217 |
num = first_time |
245 |
num = first_time |
218 |
self.add_to_ldap(name_base, "2") |
246 |
self.add_to_ldap(name_base, "2") |
219 |
return num |
247 |
return num |
|
|
248 |
|
249 |
|
250 |
if __name__ == '__main__': |
251 |
import doctest |
252 |
doctest.testmod() |