|
@@ -1,6 +1,7 @@
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
# Copyright 2016 OpenMarket Ltd
|
|
|
+# Copyright 2017 Vector Creations Ltd
|
|
|
#
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
# you may not use this file except in compliance with the License.
|
|
@@ -16,6 +17,7 @@
|
|
|
|
|
|
import logging
|
|
|
import urllib
|
|
|
+import phonenumbers
|
|
|
|
|
|
from sydent.db.valsession import ThreePidValSessionStore
|
|
|
from sydent.validators import ValidationSession, common
|
|
@@ -31,9 +33,33 @@ class MsisdnValidator:
|
|
|
self.sydent = sydent
|
|
|
self.omSms = OpenMarketSMS(sydent)
|
|
|
|
|
|
- def requestToken(self, msisdn, clientSecret, sendAttempt, nextLink):
|
|
|
+ # cache originators from config file
|
|
|
+ self.originators = {}
|
|
|
+ for opt in self.sydent.cfg.options('sms'):
|
|
|
+ if opt.startswith('originators.'):
|
|
|
+ country = opt.split('.')[1]
|
|
|
+ rawVal = self.sydent.cfg.get('sms', opt)
|
|
|
+ rawList = [i.strip() for i in rawVal.split(',')]
|
|
|
+
|
|
|
+ self.originators[country] = []
|
|
|
+ for origString in rawList:
|
|
|
+ parts = origString.split(':')
|
|
|
+ if len(parts) != 2:
|
|
|
+ raise Exception("Originators must be in form: long:<number>, short:<number> or alpha:<text>, separated by commas")
|
|
|
+ if parts[0] not in ['long', 'short', 'alpha']:
|
|
|
+ raise Exception("Invalid originator type: valid types are long, short and alpha")
|
|
|
+ self.originators[country].append({
|
|
|
+ "type": parts[0],
|
|
|
+ "text": parts[1],
|
|
|
+ })
|
|
|
+
|
|
|
+ def requestToken(self, phoneNumber, clientSecret, sendAttempt, nextLink):
|
|
|
valSessionStore = ThreePidValSessionStore(self.sydent)
|
|
|
|
|
|
+ msisdn = phonenumbers.format_number(
|
|
|
+ phoneNumber, phonenumbers.PhoneNumberFormat.E164
|
|
|
+ )[1:]
|
|
|
+
|
|
|
valSession = valSessionStore.getOrCreateTokenSession(
|
|
|
medium='msisdn', address=msisdn, clientSecret=clientSecret
|
|
|
)
|
|
@@ -44,20 +70,43 @@ class MsisdnValidator:
|
|
|
logger.info("Not texting code because current send attempt (%d) is not less than given send attempt (%s)", int(sendAttempt), int(valSession.sendAttemptNumber))
|
|
|
return valSession.id
|
|
|
|
|
|
+ smsBodyTemplate = self.sydent.cfg.get('sms', 'bodyTemplate')
|
|
|
+ originator = self.getOriginator(phoneNumber)
|
|
|
+
|
|
|
logger.info(
|
|
|
- "Attempting to text code %s to %s",
|
|
|
- valSession.token, msisdn,
|
|
|
+ "Attempting to text code %s to %s (country %d) with originator %s",
|
|
|
+ valSession.token, msisdn, phoneNumber.country_code, originator
|
|
|
)
|
|
|
|
|
|
- smsBodyTemplate = self.sydent.cfg.get('sms', 'bodyTemplate')
|
|
|
-
|
|
|
smsBody = smsBodyTemplate.format(token=valSession.token)
|
|
|
|
|
|
- self.omSms.sendTextSMS(smsBody, msisdn)
|
|
|
+ self.omSms.sendTextSMS(smsBody, msisdn, originator)
|
|
|
|
|
|
valSessionStore.setSendAttemptNumber(valSession.id, sendAttempt)
|
|
|
|
|
|
return valSession.id
|
|
|
|
|
|
+ def getOriginator(self, destPhoneNumber):
|
|
|
+ countryCode = str(destPhoneNumber.country_code)
|
|
|
+
|
|
|
+ origs = [{
|
|
|
+ "type": "alpha",
|
|
|
+ "text": "Matrix",
|
|
|
+ }]
|
|
|
+ if countryCode in self.originators:
|
|
|
+ origs = self.originators[countryCode]
|
|
|
+ elif 'default' in self.originators:
|
|
|
+ origs = self.originators['default']
|
|
|
+
|
|
|
+ # deterministically pick an originator from the list of possible
|
|
|
+ # originators, so if someone requests multiple codes, they come from
|
|
|
+ # a consistent number (if there's any chance that some originators are
|
|
|
+ # more likley to work than others, we may want to change, but it feels
|
|
|
+ # like this should be something other than just picking one randomly).
|
|
|
+ msisdn = phonenumbers.format_number(
|
|
|
+ destPhoneNumber, phonenumbers.PhoneNumberFormat.E164
|
|
|
+ )[1:]
|
|
|
+ return origs[sum([int(i) for i in msisdn]) % len(origs)]
|
|
|
+
|
|
|
def validateSessionWithToken(self, sid, clientSecret, token):
|
|
|
return common.validateSessionWithToken(self.sydent, sid, clientSecret, token)
|