acl_gen = $acl_gen; } public function register(Request $req, EntityManagerInterface $em) { $this->denyAccessUnlessGranted('mobile_user.register', null, 'No access.'); // confirm parameters $required_params = [ 'phone_model', 'os_type', 'os_version', 'phone_id' ]; // check required parameters $msg = $this->checkRequiredParameters($req, $required_params); if ($msg) return new APIResponse(false, $msg); // check if capi user already has a mobile user $mobile_user = $this->findMobileUser($em); if ($mobile_user != null) return new APIResponse(false, 'User already registered'); // retry until we get a unique id while (true) { try { // create mobile user $mobile_user = new MobileUser(); $mobile_user->setPhoneModel($req->request->get('phone_model')) ->setOSType($req->request->get('os_type')) ->setOSVersion($req->request->get('os_version')) ->setPhoneID($req->request->get('phone_id')) ->setCapiUserId($user_id); // reopen in case we get an exception if (!$em->isOpen()) { $em = $em->create( $em->getConnection(), $em->getConfiguration() ); } // save $em->persist($mobile_user); $em->flush(); } catch (DBALException $e) { error_log($e->getMessage()); // delay one second and try again sleep(1); continue; } break; } // return data // TODO: do we need to return the same names as before? // right now, still usind the old names so we use session_id name $data = [ 'session_id' => $mobile_user->getID() ]; // response return new APIResponse(true, 'Mobile user created.', $data); } public function confirmNumber(RisingTideGateway $rt, Request $req, EntityManagerInterface $em) { $this->denyAccessUnlessGranted('mobile_user.confirm.number', null, 'No access.'); // check parameters $required_params = [ 'phone_number', ]; // check required parameters $msg = $this->checkRequiredParameters($req, $required_params); if ($msg) return new APIResponse(false, $msg); // get mobile user $mobile_user = $this->findMobileUser($em); if ($mobile_user == null) return new APIResponse(false, 'No mobile user found.'); // phone number $phone_number = $req->request->get('phone_number'); // get otp_mode from .env $dotenv = new Dotenv(); $dotenv->loadEnv(__DIR__.'/../../../.env'); $otp_mode = $_ENV['OTP_MODE']; // check for hardcoded phone number for app store testing if ($phone_number == '639221111111') { $code = '123456'; $mobile_user->setConfirmCode($code) ->setPhoneNumber($phone_number); $em->flush(); return new APIResponse(true, 'Number confirmed.'); } // check if otp_mode is test if ($otp_mode == 'test') { $code = '123456'; $mobile_user->setConfirmCode($code) ->setPhoneNumber($phone_number); $em->flush(); return new APIResponse(true, 'Number confirmed.'); } // TODO: spam protection // TODO: validate phone number // generate code and save $code = $this->generateConfirmCode(); $mobile_user->setConfirmCode($code) ->setPhoneNumber($phone_number); $em->flush(); if ($otp_mode != 'test') { // send sms to number $this->sendConfirmationCode($rt, $phone_number, $code); } // response return new APIResponse(true, 'Number confirmed.'); } public function validateCode(Request $req, EntityManagerInterface $em) { $this->denyAccessUnlessGranted('mobile_user.validate.code', null, 'No access.'); // check parameters $required_params = [ 'code', ]; // check required parameters $msg = $this->checkRequiredParameters($req, $required_params); if ($msg) return new APIResponse(false, $msg); // get mobile user $mobile_user = $this->findMobileUser($em); if ($mobile_user == null) return new APIResponse(false, 'No mobile user found.'); // code is wrong $code = $req->request->get('code'); if ($mobile_user->getConfirmCode() != $code) return new APIResponse(false, 'Wrong confirm code'); // set confirm date $date = new DateTime(); $mobile_user->setDateConfirmed($date) ->setConfirmed(); // TODO: check if we have the number registered before and merge $dupe_user = $this->findNumberMobileUser($mobile_user->getPhoneNumber(), $em); if ($dupe_user != null) { $dupe_cust = $dupe_user->getCustomer(); $mobile_user->setCustomer($dupe_cust); } // TODO: check if mobile matches mobile of customer $customer = $this->findCustomerByNumber($mobile_user->getPhoneNumber(), $em); if ($customer != null) { // TODO: if there is a dupe_sess, do we need to check if // dupe_cust is the same as the customer we found? $mobile_user->setCustomer($customer); } $em->flush(); // response return new APIResponse(true, 'Code validated'); } public function getInfo(Request $req, EntityManagerInterface $em) { $this->denyAccessUnlessGranted('mobile_user.get.info', null, 'No access.'); // get mobile user $mobile_user = $this->findMobileUser($em); if ($mobile_user == null) return new APIResponse(false, 'No mobile user found.'); // if no customer found $cust = $mobile_user->getCustomer(); if ($cust == null) { $data = [ 'first_name' => '', 'last_name' => '', 'priv_third_party' => (bool) false, 'priv_promo' => (bool) false, ]; return new APIResponse(true, 'No customer info found', $data); } // send back customer details $data = [ 'first_name' => $cust->getFirstName(), 'last_name' => $cust->getLastName(), 'priv_third_party' => (bool) $cust->getPrivacyThirdParty(), 'priv_promo' => (bool) $cust->getPrivacyPromo(), ]; return new APIResponse(true, 'Customer info found', $data); } public function updateInfo(Request $req, EntityManagerInterface $em) { $this->denyAccessUnlessGranted('mobile_user.update.info', null, 'No access.'); // check required parameters $required_params = [ 'first_name', 'last_name', ]; // check required parameters $msg = $this->checkRequiredParameters($req, $required_params); if ($msg) return new APIResponse(false, $msg); // get mobile user $mobile_user = $this->findMobileUser($em); if ($mobile_user == null) return new APIResponse(false, 'No mobile user found.'); $cust = $this->updateCustomerInfo($req, $em, $mobile_user); // get privacy policy for mobile $dotenv = new Dotenv(); $dotenv->loadEnv(__DIR__.'/../../../.env'); $policy_mobile_id = $_ENV['POLICY_MOBILE']; $mobile_policy = $em->getRepository(PrivacyPolicy::class)->find($policy_mobile_id); // set policy id if ($mobile_policy != null) { $cust->setPrivacyPolicyMobile($mobile_policy); } $em->flush(); return new APIResponse(true, 'Customer info updated'); } public function getStatus(Request $req, EntityManagerInterface $em) { $this->denyAccessUnlessGranted('mobile_user.get.status', null, 'No access.'); // get mobile user $mobile_user = $this->findMobileUser($em); if ($mobile_user == null) return new APIResponse(false, 'No mobile user found.'); // set data $data = []; if ($mobile_user->isConfirmed()) $data['status'] = 'confirmed'; else $data['status'] = 'unconfirmed'; return new APIResponse(true, 'Customer status', $data); } public function resendCode(Request $req, RisingTideGateway $rt, EntityManagerInterface $em) { $this->denyAccessUnlessGranted('mobile_user.resend.code', null, 'No access.'); // get mobile user $mobile_user = $this->findMobileUser($em); if ($mobile_user == null) return new APIResponse(false, 'No mobile user found.'); // already confirmed if ($mobile_user->isConfirmed()) return new APIResponse(true, 'User is already confirmed'); // have sent code before if ($mobile_session->getDateCodeSent() != null) return new APIResponse(true, 'Can only send confirm code every 5 mins'); // TODO: send via sms $phone_number = $mobile_user->getPhoneNumber(); $code = $mobile_user->getConfirmCode(); $this->sendConfirmationCode($rt, $phone_number, $code); return new APIResponse(true, 'Code re-sent'); } public function versionCheck(Request $req, EntityManagerInterface $em) { $this->denyAccessUnlessGranted('mobile_user.version.check', null, 'No access.'); $required_params = [ 'version', ]; // check required parameters $msg = $this->checkRequiredParameters($req, $required_params); if ($msg) return new APIResponse(false, $msg); // get mobile user $mobile_user = $this->findMobileUser($em); if ($mobile_user == null) return new APIResponse(false, 'No mobile user found.'); $need_update = false; $msg = 'Version is up to date.'; $api_version = $this->getParameter('api_version'); $app_version = $req->query->get('version'); $api_v = explode('.', $api_version); $app_v = explode('.', $app_version); if ($api_v[0] < $app_v[0]) return new APIResponse(false, 'Invalid application version: ' . $app_version); if ($api_v[0] > $app_v[0]) { $need_update = true; $msg = 'Your version is outdated and needs an update to use the latest features RES-Q has to offer.'; } $data = [ 'need_update' => $need_update, 'message' => $msg, ]; return new APIResponse(true, 'Version checked', $data); } public function updateDeviceID(Request $req, EntityManagerInterface $em) { $this->denyAccessUnlessGranted('mobile_user.update.deviceid', null, 'No access.'); $required_params = [ 'device_id', ]; // check required parameters $msg = $this->checkRequiredParameters($req, $required_params); if ($msg) return new APIResponse(false, $msg); // get mobile user $mobile_user = $this->findMobileUser($em); if ($mobile_user == null) return new APIResponse(false, 'No mobile user found.'); $device_id = $req->request->get('device_id'); $mobile_user->setDevicePushID($device_id); $em->flush(); // response return new APIResponse(true, 'Device ID updated'); } public function privacySettings(Request $req, EntityManagerInterface $em) { $this->denyAccessUnlessGranted('mobile_user.privacy.settings', null, 'No access.'); $required_params = [ 'priv_third_party', // 'priv_promo', ]; // check required parameters $msg = $this->checkRequiredParameters($req, $required_params); if ($msg) return new APIResponse(false, $msg); // get mobile user $mobile_user = $this->findMobileUser($em); if ($mobile_user == null) return new APIResponse(false, 'No mobile user found.'); // get customer $cust = $mobile_user->getCustomer(); if ($cust == null) return new APIResponse(false, 'No customer information found'); // set privacy settings $priv_promo = $req->request->get('priv_promo', false); $priv_third_party = $req->request->get('priv_third_party'); $cust->setPrivacyThirdParty($priv_third_party) ->setPrivacyPromo($priv_promo); // get the policy ids from .env $dotenv = new Dotenv(); $dotenv->loadEnv(__DIR__.'/../../../.env'); $policy_promo_id = $_ENV['POLICY_PROMO']; $policy_third_party_id = $_ENV['POLICY_THIRD_PARTY']; // check if privacy settings are true // if true, set the private policy for the customer if ($priv_promo) { // find the promo policy $policy = $em->getRepository(PrivacyPolicy::class)->find($policy_promo_id); // set policy id if ($policy != null) { $cust->setPrivacyPolicyPromo($policy); } } if ($priv_third_party) { // find the third party policy $policy = $em->getRepository(PrivacyPolicy::class)->find($policy_third_party_id); // set policy id if ($policy != null) { $cust->setPrivacyPolicyThirdParty($policy); } } $em->flush(); return new APIResponse(true, 'Privacy policy settings set'); } protected function findMobileUser($em) { // get capi user to link to mobile user $user_id = $this->getUser()->getID(); $mobile_user = $em->getRepository(MobileUser::class)->findOneBy(['capi_user_id' => $user_id]); return $mobile_user; } // TODO: find session customer by phone number protected function findNumberMobileUser($number, $em) { $query = $em->getRepository(MobileUser::class)->createQueryBuilder('s') ->where('s.phone_number = :number') ->andWhere('s.customer is not null') ->andWhere('s.confirm_flag = 1') ->setParameter('number', $number) ->setMaxResults(1) ->getQuery(); // we just need one $res = $query->getOneOrNullResult(); return $res; } protected function findCustomerByNumber($number, $em) { $customers = $em->getRepository(Customer::class)->findBy(['phone_mobile' => $number]); // find the customer with the most number of cars $car_count = 0; $cust = null; foreach($customers as $customer) { $vehicles = $customer->getVehicles(); if (count($vehicles) > $car_count) { $car_count = count($vehicles); // "save" customer object $cust = $customer; } } return $cust; } protected function updateCustomerInfo($req, $em, $mobile_user) { // create new customer if it's not there $cust = $mobile_user->getCustomer(); if ($cust == null) { $cust = new Customer(); // set customer source $cust->setCreateSource(CustomerSource::MOBILE); $em->persist($cust); $mobile_user->setCustomer($cust); } $cust->setFirstName($req->request->get('first_name')) ->setLastName($req->request->get('last_name')) ->setEmail($req->request->get('email', '')) ->setConfirmed($mobile_user->isConfirmed()); // update mobile phone of customer $cust->setPhoneMobile(substr($mobile_user->getPhoneNumber(), 2)); return $cust; } protected function sendConfirmationCode(RisingTideGateway $rt, $phone_number, $code) { // send sms to number $message = "Your Resq confirmation code is $code."; $rt->sendSMS($phone_number, 'MOTOLITE', $message); } // TODO: this might not be needed if we use APIController's checkRequiredParameters // or we put this into a service? protected function checkMissingParameters(Request $req, $params = []) { $missing = []; // check if parameters are there foreach ($params as $param) { if ($req->getMethod() == 'GET') { $check = $req->query->get($param); if (empty($check)) $missing[] = $param; } else if ($req->getMethod() == 'POST') { $check = $req->request->get($param); if (empty($check)) $missing[] = $param; } else return $params; } return $missing; } // TODO: since we broke the functions into separate files, we need // to figure out how to make this accessible to all ResqAPI controllers protected function checkParamsAndKey(Request $req, $em, $params) { // TODO: depends on what we decide to return // returns APIResult object $res = new APIResult(); // check for api_key in query string $api_key = $req->query->get('api_key'); if (empty($api_key)) { $res->setError(true) ->setErrorMessage('Missing API key'); return $res; } // check missing parameters $missing = $this->checkMissingParameters($req, $params); if (count($missing) > 0) { $miss_string = implode(', ', $missing); $res->setError(true) ->setErrorMessage('Missing parameter(s): ' . $miss_string); return $res; } // check api key $mobile_user = $this->checkAPIKey($em, $req->query->get('api_key')); if ($mobile_user == null) { $res->setError(true) ->setErrorMessage('Invalid API Key'); return $res; } // store session $this->session = $sess; return $res; } // TODO: type hint entity manager // TODO: since we broke the functions into separate files, we need // to figure out how to make this accessible to all ResqAPI controllers protected function checkAPIKey($em, $api_key) { // find the api key (session id) // TODO: user validation needs to be changed $m_user = $em->getRepository(MobileUser::class)->find($api_key); if ($m_user == null) return null; return $m_user; } }