# oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. # Copyright (c) 2018, Gluu # # Author: Jose Gonzalez # Author: Gasmyr Mougang from org.gluu.service.cdi.util import CdiUtil from org.gluu.oxauth.security import Identity from org.gluu.model.custom.script.type.auth import PersonAuthenticationType from org.gluu.oxauth.service import AuthenticationService from org.gluu.oxauth.service.common import UserService from org.gluu.oxauth.service import SessionIdService from org.gluu.oxauth.util import ServerUtil from org.gluu.util import StringHelper, ArrayHelper from java.util import Arrays from javax.faces.application import FacesMessage from org.gluu.jsf2.message import FacesMessages import com.twilio.Twilio as Twilio import com.twilio.rest.api.v2010.account.Message as Message import com.twilio.type.PhoneNumber as PhoneNumber import org.codehaus.jettison.json.JSONArray as JSONArray import java import random import jarray import sys class PersonAuthentication(PersonAuthenticationType): def __init__(self, currentTimeMillis): self.currentTimeMillis = currentTimeMillis self.mobile_number = None self.identity = CdiUtil.bean(Identity) def init(self, customScript, configurationAttributes): print "==============================================" print "===TWILIO SMS INITIALIZATION==================" print "==============================================" self.ACCOUNT_SID = None self.AUTH_TOKEN = None self.FROM_NUMBER = None # Get Custom Properties try: self.ACCOUNT_SID = configurationAttributes.get("twilio_sid").getValue2() except: print 'TwilioSMS, Missing required configuration attribute "twilio_sid"' try: self.AUTH_TOKEN = configurationAttributes.get("twilio_token").getValue2() except: print'TwilioSMS, Missing required configuration attribute "twilio_token"' try: self.FROM_NUMBER = configurationAttributes.get("from_number").getValue2() except: print'TwilioSMS, Missing required configuration attribute "from_number"' if None in (self.ACCOUNT_SID, self.AUTH_TOKEN, self.FROM_NUMBER): print "twilio_sid, twilio_token, from_number is empty ... returning False" return False print "===TWILIO SMS INITIALIZATION DONE PROPERLY=====" return True def destroy(self, configurationAttributes): print "Twilio SMS. Destroy" print "Twilio SMS. Destroyed successfully" return True def getApiVersion(self): return 11 def getAuthenticationMethodClaims(self, requestParameters): return None def isValidAuthenticationMethod(self, usageType, configurationAttributes): return True def getAlternativeAuthenticationMethod(self, usageType, configurationAttributes): return None def authenticate(self, configurationAttributes, requestParameters, step): print "==============================================" print "====TWILIO SMS AUTHENCATION===================" print "==============================================" userService = CdiUtil.bean(UserService) authenticationService = CdiUtil.bean(AuthenticationService) sessionIdService = CdiUtil.bean(SessionIdService) facesMessages = CdiUtil.bean(FacesMessages) facesMessages.setKeepMessages() session_attributes = self.identity.getSessionId().getSessionAttributes() form_passcode = ServerUtil.getFirstValue(requestParameters, "passcode") form_name = ServerUtil.getFirstValue(requestParameters, "TwilioSmsloginForm") print "TwilioSMS. form_response_passcode: %s" % str(form_passcode) if step == 1: print "==============================================" print "=TWILIO SMS STEP 1 | Password Authentication==" print "==============================================" credentials = self.identity.getCredentials() user_name = credentials.getUsername() user_password = credentials.getPassword() logged_in = False if StringHelper.isNotEmptyString(user_name) and StringHelper.isNotEmptyString(user_password): logged_in = authenticationService.authenticate(user_name, user_password) if not logged_in: return False # Get the Person's number and generate a code foundUser = None try: foundUser = authenticationService.getAuthenticatedUser() except: print 'TwilioSMS, Error retrieving user %s from LDAP' % (user_name) return False try: isVerified = foundUser.getAttribute("phoneNumberVerified") if isVerified: self.mobile_number = foundUser.getAttribute("employeeNumber") if self.mobile_number is None: mobile_numbers_json = foundUser.getAttribute("mobile", True, True) if mobile_numbers_json is not None: if mobile_numbers_json is not None: self.mobile_number = mobile_numbers_json.get(0) if self.mobile_number is None: self.mobile_number = foundUser.getAttribute("telephoneNumber") if self.mobile_number is None: print "TwilioSMS, Error finding mobile number for user '%s'" % user_name except: facesMessages.add(FacesMessage.SEVERITY_ERROR, "Failed to determine mobile phone number") print "TwilioSMS, Error finding mobile number for '%s'. Exception: '%s'" % (user_name, sys.exc_info()[1]) return False # Generate Random six digit code and store it in array code = random.randint(100000, 999999) # Get code and save it in LDAP temporarily with special session entry self.identity.setWorkingParameter("code", code) sessionId = sessionIdService.getSessionId() # fetch from persistence sessionId.getSessionAttributes().put("code", code) try: Twilio.init(self.ACCOUNT_SID, self.AUTH_TOKEN); message = Message.creator(PhoneNumber(self.mobile_number), PhoneNumber(self.FROM_NUMBER), str(code)).create(); print "++++++++++++++++++++++++++++++++++++++++++++++" print 'TwilioSMs, Message Sid: %s' % (message.getSid()) print 'TwilioSMs, User phone: %s' % (self.mobile_number) print "++++++++++++++++++++++++++++++++++++++++++++++" sessionId.getSessionAttributes().put("mobile_number", self.mobile_number) sessionId.getSessionAttributes().put("mobile", self.mobile_number) sessionIdService.updateSessionId(sessionId) self.identity.setWorkingParameter("mobile_number", self.mobile_number) self.identity.getSessionId().getSessionAttributes().put("mobile_number",self.mobile_number) self.identity.setWorkingParameter("mobile", self.mobile_number) self.identity.getSessionId().getSessionAttributes().put("mobile",self.mobile_number) print "++++++++++++++++++++++++++++++++++++++++++++++" print "Number: %s" % (self.identity.getWorkingParameter("mobile_number")) print "Mobile: %s" % (self.identity.getWorkingParameter("mobile")) print "++++++++++++++++++++++++++++++++++++++++++++++" print "========================================" print "===TWILIO SMS FIRST STEP DONE PROPERLY==" print "========================================" return True except Exception, ex: facesMessages.add(FacesMessage.SEVERITY_ERROR, "Failed to send message to mobile phone") print "TwilioSMS. Error sending message to Twilio" print "TwilioSMS. Unexpected error:", ex return False elif step == 2: # Retrieve the session attribute print "==============================================" print "=TWILIO SMS STEP 2 | Password Authentication==" print "==============================================" code = session_attributes.get("code") print '=======> Session code is "%s"' % str(code) sessionIdService = CdiUtil.bean(SessionIdService) sessionId = sessionIdService.getSessionId() # fetch from persistence code = sessionId.getSessionAttributes().get("code") print '=======> Database code is "%s"' % str(code) self.identity.setSessionId(sessionId) print "==============================================" print "TwilioSMS. Code: %s" % str(code) print "==============================================" if code is None: print "TwilioSMS. Failed to find previously sent code" return False if form_passcode is None: print "TwilioSMS. Passcode is empty" return False if len(form_passcode) != 6: print "TwilioSMS. Passcode from response is not 6 digits: %s" % form_passcode return False if form_passcode == code: print "TiwlioSMS, SUCCESS! User entered the same code!" print "========================================" print "===TWILIO SMS SECOND STEP DONE PROPERLY" print "========================================" return True print "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" print "TwilioSMS. FAIL! User entered the wrong code! %s != %s" % (form_passcode, code) print "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" facesMessages.add(FacesMessage.SEVERITY_ERROR, "Incorrect Twilio code, please try again.") print "================================================" print "===TWILIO SMS SECOND STEP FAILED: INCORRECT CODE" print "================================================" return False print "TwilioSMS. ERROR: step param not found or != (1|2)" return False def prepareForStep(self, configurationAttributes, requestParameters, step): if step == 1: print "TwilioSMS. Prepare for Step 1" return True elif step == 2: print "TwilioSMS. Prepare for Step 2" return True return False def getExtraParametersForStep(self, configurationAttributes, step): if step == 2: return Arrays.asList("code") return None def getCountAuthenticationSteps(self, configurationAttributes): return 2 def getPageForStep(self, configurationAttributes, step): if step == 2: return "/auth/otp_sms/otp_sms.xhtml" return "" def getNextStep(self, configurationAttributes, requestParameters, step): return -1 def getLogoutExternalUrl(self, configurationAttributes, requestParameters): print "Get external logout URL call" return None def logout(self, configurationAttributes, requestParameters): return True