0002-sms-return-filtered-numbers-to-caller-39650.patch
passerelle/sms/models.py | ||
---|---|---|
142 | 142 |
if SMSResource.BE_ in self.authorized: |
143 | 143 |
regexes.append(r'^00324[5-9]\d{7}$') # Belgian |
144 | 144 |
country_regex = re.compile('|'.join(regexes)) |
145 | 145 | |
146 | 146 |
if not self.allow_premium_rate: |
147 | 147 |
premium_numbers = set(dest for dest in destinations if premium_regex.match(dest)) |
148 | 148 |
if SMSResource.ALL not in self.authorized: |
149 | 149 |
foreign_numbers = set(dest for dest in destinations if not country_regex.match(dest)) |
150 |
for number in list(premium_numbers): |
|
151 |
logging.warning('unauthorized premium rate phone number: %s', number) |
|
152 |
for number in list(foreign_numbers - premium_numbers): |
|
153 |
logging.warning('unauthorized foreign phone number: %s', number) |
|
154 |
authorized_numbers = list(set(destinations) - foreign_numbers - premium_numbers) |
|
150 | ||
151 |
authorized_numbers = sorted( |
|
152 |
set(destinations) - foreign_numbers - premium_numbers, |
|
153 |
key=int) |
|
154 |
premium_numbers_string = ", ".join(sorted(premium_numbers, key=int)) |
|
155 |
foreign_numbers_string = ", ".join(sorted(foreign_numbers - premium_numbers, key=int)) |
|
156 |
if premium_numbers_string: |
|
157 |
logging.warning('unauthorized premium rate phone number: %s', |
|
158 |
premium_numbers_string) |
|
159 |
if foreign_numbers_string: |
|
160 |
logging.warning('unauthorized foreign phone number: %s', |
|
161 |
foreign_numbers_string) |
|
155 | 162 |
if len(authorized_numbers) == 0: |
156 | 163 |
raise APIError('no phone number was authorized: %s' % ', '.join(destinations)) |
157 |
return authorized_numbers |
|
164 |
warnings = { |
|
165 |
'deny premium rate phone numbers': premium_numbers_string, |
|
166 |
'deny foreign phone numbers': foreign_numbers_string, |
|
167 |
} |
|
168 |
return authorized_numbers, warnings |
|
158 | 169 | |
159 | 170 |
@endpoint(perm='can_send_messages', methods=['post'], |
160 | 171 |
description=_('Send a SMS message'), |
161 | 172 |
parameters={'nostop': {'description': _('Do not send STOP instruction'), 'example_value': '1'}}, |
162 | 173 |
post={'request_body': {'schema': {'application/json': SEND_SCHEMA}}}) |
163 | 174 |
def send(self, request, post_data, nostop=None): |
164 | 175 |
post_data['message'] = post_data['message'][:self.max_message_length] |
165 | 176 |
post_data['to'] = self.clean_numbers(post_data['to']) |
166 |
post_data['to'] = self.authorize_numbers(post_data['to']) |
|
177 |
post_data['to'], warnings = self.authorize_numbers(post_data['to'])
|
|
167 | 178 |
logging.info('sending SMS to %r from %r', post_data['to'], post_data['from']) |
168 | 179 |
stop = nostop is None # ?nostop in not in query string |
169 | 180 |
self.add_job('send_job', |
170 | 181 |
text=post_data['message'], sender=post_data['from'], destinations=post_data['to'], |
171 | 182 |
stop=stop) |
172 |
return {'err': 0} |
|
183 |
return {'err': 0, 'warn': warnings}
|
|
173 | 184 | |
174 | 185 |
def send_job(self, *args, **kwargs): |
175 | 186 |
self.send_msg(**kwargs) |
176 | 187 |
SMSLog.objects.create(appname=self.get_connector_slug(), slug=self.slug) |
177 | 188 | |
178 | 189 |
class Meta: |
179 | 190 |
abstract = True |
180 | 191 |
passerelle/sms/views.py | ||
---|---|---|
24 | 24 | |
25 | 25 |
def form_valid(self, form): |
26 | 26 |
number = form.cleaned_data['number'] |
27 | 27 |
sender = form.cleaned_data['sender'] |
28 | 28 |
message = form.cleaned_data['message'] |
29 | 29 |
connector = self.get_object() |
30 | 30 |
try: |
31 | 31 |
number = connector.clean_numbers([number])[0] |
32 |
number = connector.authorize_numbers([number])[0] |
|
32 |
number = connector.authorize_numbers([number])[0][0]
|
|
33 | 33 |
connector.send_msg( |
34 | 34 |
text=message, sender=sender, destinations=[number], stop=False) |
35 | 35 |
except APIError as exc: |
36 | 36 |
messages.error(self.request, _('Sending SMS fails: %s' % exc)) |
37 | 37 |
else: |
38 | 38 |
messages.success(self.request, _('An SMS was just sent')) |
39 | 39 |
return super(SmsTestSendView, self).form_valid(form) |
tests/test_sms.py | ||
---|---|---|
57 | 57 | |
58 | 58 |
def test_authorize_numbers(): |
59 | 59 |
connector = OVHSMSGateway() |
60 | 60 | |
61 | 61 |
# premium-rate |
62 | 62 |
assert connector.allow_premium_rate == False |
63 | 63 |
number = '0033' + '8' + '12345678' |
64 | 64 |
with pytest.raises(APIError, match='no phone number was authorized: %s' % number): |
65 |
assert connector.authorize_numbers([number])
|
|
65 |
connector.authorize_numbers([number]) |
|
66 | 66 |
connector.allow_premium_rate = True |
67 | 67 |
connector.save() |
68 |
assert connector.authorize_numbers([number])[0] == [number] |
|
68 | 69 | |
69 | 70 |
# All country |
70 | 71 |
assert connector.authorized == [SMSResource.ALL] |
71 | 72 |
number = '0033' + '1' + '12345678' |
72 |
assert connector.authorize_numbers([number]) == [number] |
|
73 |
assert connector.authorize_numbers([number])[0] == [number]
|
|
73 | 74 |
connector.authorized = [SMSResource.FR_METRO] |
74 | 75 |
connector.save() |
75 | 76 |
with pytest.raises(APIError, match='no phone number was authorized: %s' % number): |
76 | 77 |
connector.authorize_numbers([number]) |
77 | 78 | |
78 | 79 |
# France |
79 | 80 |
number = '0033' + '6' + '12345678' |
80 |
assert connector.authorize_numbers([number]) == [number] |
|
81 |
assert connector.authorize_numbers([number])[0] == [number]
|
|
81 | 82 |
connector.authorized = [SMSResource.FR_DOMTOM] |
82 | 83 |
connector.save() |
83 | 84 |
with pytest.raises(APIError, match='no phone number was authorized: %s' % number): |
84 | 85 |
connector.authorize_numbers([number]) |
85 | 86 | |
86 | 87 |
# Dom-Tom |
87 | 88 |
number = '596596' + '123456' |
88 |
assert connector.authorize_numbers([number]) == [number] |
|
89 |
assert connector.authorize_numbers([number])[0] == [number]
|
|
89 | 90 |
connector.authorized = [SMSResource.BE_] |
90 | 91 |
connector.save() |
91 | 92 |
with pytest.raises(APIError, match='no phone number was authorized: %s' % number): |
92 | 93 |
connector.authorize_numbers([number]) |
93 | 94 | |
94 | 95 |
# Belgian |
95 | 96 |
number = '0032' + '45' + '1234567' |
96 |
assert connector.authorize_numbers([number]) == [number] |
|
97 |
assert connector.authorize_numbers([number])[0] == [number]
|
|
97 | 98 |
connector.authorized = [SMSResource.FR_METRO] |
98 | 99 |
connector.save() |
99 | 100 |
with pytest.raises(APIError, match='no phone number was authorized: %s' % number): |
100 | 101 |
connector.authorize_numbers([number]) |
101 | 102 | |
102 |
# Don't raise if filtered destination is not empty |
|
103 |
numbers = ['0033' + '6' + '12345678', '0032' + '45' + '1234567'] |
|
104 |
assert len(connector.authorize_numbers(numbers)) == 1 |
|
103 |
# Don't raise if authorized destinations are not empty |
|
104 |
connector.allow_premium_rate = False |
|
105 |
connector.authorized = [SMSResource.FR_METRO] |
|
106 |
connector.save() |
|
107 |
numbers = [ |
|
108 |
'0033' + '8' + '12345678', |
|
109 |
'0033' + '1' + '12345678', |
|
110 |
'0033' + '6' + '12345678', |
|
111 |
'596596' + '123456', |
|
112 |
'0032' + '45' + '1234567', |
|
113 |
] |
|
114 |
authorized_numbers, warnings = connector.authorize_numbers(numbers) |
|
115 |
assert authorized_numbers == ['0033612345678'] |
|
116 |
assert warnings == { |
|
117 |
'deny premium rate phone numbers': '0033812345678', |
|
118 |
'deny foreign phone numbers': '0032451234567, 0033112345678, 596596123456', |
|
119 |
} |
|
105 | 120 | |
106 | 121 | |
107 | 122 |
@pytest.fixture(params=klasses) |
108 | 123 |
def connector(request, db): |
109 | 124 |
klass = request.param |
110 | 125 |
kwargs = getattr(klass, 'TEST_DEFAULTS', {}).get('create_kwargs', {}) |
111 | 126 |
kwargs.update({ |
112 | 127 |
'title': klass.__name__, |
... | ... | |
497 | 512 |
if x.text.startswith(_('Authorized Countries'))][0] |
498 | 513 | |
499 | 514 |
path = '/%s/%s/send/' % (connector.get_connector_slug(), connector.slug) |
500 | 515 |
payload = { |
501 | 516 |
'message': 'plop', |
502 | 517 |
'from': '+33699999999', |
503 | 518 |
'to': ['+33688888888'], |
504 | 519 |
} |
505 |
app.post_json(path, params=payload) |
|
520 |
resp = app.post_json(path, params=payload) |
|
521 |
assert resp.json['warn'] == { |
|
522 |
'deny premium rate phone numbers': '', |
|
523 |
'deny foreign phone numbers': '', |
|
524 |
} |
|
506 | 525 |
with mock.patch.object(type(connector), 'send_msg') as send_function: |
507 | 526 |
send_function.return_value = {} |
508 | 527 |
connector.jobs() |
509 | 528 |
assert SMSLog.objects.count() == 1 |
510 | 529 | |
511 | 530 |
payload['to'][0] = '+33188888888' |
512 | 531 |
SMSLog.objects.all().delete() |
513 | 532 |
app.post_json(path, params=payload) |
514 |
- |