AccountInterface.class.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. <?php
  2. namespace Models {
  3. require_once 'Models/User.class.php';
  4. require_once 'Tools/Crypto.class.php';
  5. class AccountInterface {
  6. //
  7. protected $DataInterface;
  8. /**
  9. *
  10. */
  11. public function __construct($DataInterface) {
  12. $this->DataInterface = $DataInterface;
  13. }
  14. /**
  15. * Get data required for signing up new account.
  16. */
  17. public function accountSignupGet($lang) {
  18. $statement = $this->DataInterface->DatabaseConnection->prepare(
  19. "SELECT *, name_$lang AS name FROM country ORDER BY name_$lang"
  20. );
  21. if(!$statement->execute()) {
  22. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  23. }
  24. $countries = $statement->fetchAll(\PDO::FETCH_ASSOC);
  25. return [
  26. 'result' => 'OK',
  27. 'countries' => $countries
  28. ];
  29. }
  30. /**
  31. *
  32. */
  33. public function accountResetPost($data) {
  34. $statement = $this->DataInterface->DatabaseConnection->prepare(
  35. "SELECT address FROM email WHERE type = 'postmaster'"
  36. );
  37. if(!$statement->execute()) {
  38. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  39. }
  40. $postmaster = $statement->fetchAll(\PDO::FETCH_ASSOC)[0]['address'];
  41. // user
  42. if (!filter_var($data['to'], FILTER_VALIDATE_EMAIL)) {
  43. return ['result' => 'ERROR', 'reason' => 'invalid_email'];
  44. }
  45. // Check email exists
  46. $statement = $this->DataInterface->DatabaseConnection->prepare(
  47. 'SELECT ID FROM user WHERE email = \''.$data['to'].'\''
  48. );
  49. $statement->execute();
  50. if(count($statement->fetchAll(\PDO::FETCH_ASSOC))==0) {
  51. return ['result' => 'ERROR', 'reason' => 'invalid_email'];
  52. }
  53. //
  54. $reset_token = \Tools\UUID::v4();
  55. $statement = $this->DataInterface->DatabaseConnection->prepare(
  56. 'UPDATE user SET password = \''.$reset_token.'\' WHERE email = \''.$data['to'].'\''
  57. );
  58. if(!$statement->execute()) {
  59. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  60. }
  61. //
  62. return [
  63. 'result' => 'OK',
  64. 'email' => $data['to'],
  65. 'sender' => $postmaster,
  66. 'reset_token' => $reset_token
  67. ];
  68. }
  69. /**
  70. *
  71. */
  72. public function accountReset2Post($data) {
  73. if (strlen($data['newPassword']) < 8 ||
  74. !preg_match("#[0-9]+#", $data['newPassword']) ||
  75. !preg_match("#[a-z]+#", $data['newPassword']) ||
  76. !preg_match("#[A-Z]+#", $data['newPassword'])) {
  77. return ['result' => 'ERROR', 'reason' => 'password_strength'];
  78. }
  79. $password = \Tools\Crypto::getHashPassword($data['newPassword']);
  80. $statement = $this->DataInterface->DatabaseConnection->prepare(
  81. "UPDATE user SET password = :newPassword WHERE password = :token"
  82. );
  83. $statement->bindParam(':newPassword', $password);
  84. $statement->bindParam(':token', $data['token']);
  85. if(!$statement->execute()) {
  86. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  87. }
  88. //
  89. return [
  90. 'result' => 'OK'
  91. ];
  92. }
  93. /**
  94. * Check data and create new account.
  95. */
  96. public function accountSignupPost($data) {
  97. $statement = $this->DataInterface->DatabaseConnection->prepare(
  98. "SELECT address FROM email WHERE type = 'postmaster'"
  99. );
  100. if(!$statement->execute()) {
  101. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  102. }
  103. $email = $statement->fetchAll(\PDO::FETCH_ASSOC)[0]['address'];
  104. // user
  105. if (!filter_var($data['user']['email'], FILTER_VALIDATE_EMAIL)) {
  106. return ['result' => 'ERROR', 'group' => 'user', 'reason' => 'email'];
  107. }
  108. // Check email exists
  109. $statement = $this->DataInterface->DatabaseConnection->prepare(
  110. 'SELECT ID FROM user WHERE email = \''.$data['user']['email'].'\' AND type = \'physician\''
  111. );
  112. $statement->execute();
  113. if(count($statement->fetchAll(\PDO::FETCH_ASSOC))!=0) {
  114. return ['result' => 'ERROR', 'reason' => 'duplicate_user'];
  115. }
  116. if (strlen($data['user']['password']) < 8 ||
  117. !preg_match("#[0-9]+#", $data['user']['password']) ||
  118. !preg_match("#[a-z]+#", $data['user']['password']) ||
  119. !preg_match("#[A-Z]+#", $data['user']['password'])) {
  120. return ['result' => 'ERROR', 'reason' => 'password_strength'];
  121. }
  122. if($data['user']['password'] != $data['user']['password2']) {
  123. return ['result' => 'ERROR', 'reason' => 'password_mismatch'];
  124. }
  125. if(empty($data['user']['firstname'])) {
  126. return ['result' => 'ERROR', 'group' => 'user', 'reason' => 'firstname'];
  127. }
  128. if(empty($data['user']['lastname'])) {
  129. return ['result' => 'ERROR', 'group' => 'user', 'reason' => 'lastname'];
  130. }
  131. if(empty($data['user']['phone'])) {
  132. return ['result' => 'ERROR', 'group' => 'user', 'reason' => 'phone'];
  133. }
  134. // organization
  135. if(empty($data['organization']['name'])) {
  136. return ['result' => 'ERROR', 'group' => 'organization', 'reason' => 'name'];
  137. }
  138. if(empty($data['organization']['country'])) {
  139. return ['result' => 'ERROR', 'group' => 'organization', 'reason' => 'country'];
  140. }
  141. if(empty($data['organization']['zip'])) {
  142. return ['result' => 'ERROR', 'group' => 'organization', 'reason' => 'zip'];
  143. }
  144. if(empty($data['organization']['city'])) {
  145. return ['result' => 'ERROR', 'group' => 'organization', 'reason' => 'city'];
  146. }
  147. if(empty($data['organization']['address'])) {
  148. return ['result' => 'ERROR', 'group' => 'organization', 'reason' => 'address'];
  149. }
  150. if(empty($data['organization']['phone'])) {
  151. return ['result' => 'ERROR', 'group' => 'organization', 'reason' => 'phone'];
  152. }
  153. // probe
  154. if(empty($data['probe']['brand'])) {
  155. return ['result' => 'ERROR', 'group' => 'probe', 'reason' => 'brand'];
  156. }
  157. if(empty($data['probe']['year'])) {
  158. $data['probe']['year'] = null;
  159. }
  160. if(empty($data['probe']['frequency'])) {
  161. return ['result' => 'ERROR', 'group' => 'probe', 'reason' => 'frequency'];
  162. }
  163. // validation
  164. if($data['validation']['terms'] != 'true') {
  165. return ['result' => 'ERROR', 'reason' => 'validation_terms'];
  166. }
  167. /*if($data['validation']['database'] != 'true') {
  168. return ['result' => 'ERROR', 'reason' => 'validation_database'];
  169. }*/
  170. // Begin transaction
  171. $this->DataInterface->DatabaseConnection->beginTransaction();
  172. // Insert user
  173. $statement = $this->DataInterface->DatabaseConnection->prepare(
  174. "INSERT INTO user(activation_token, activation_expire, password, firstname, lastname, email, phone, type) VALUES(:activation_token, :activation_expire, :password, :firstname, :lastname, :email, :phone, 'physician')"
  175. );
  176. $activation_token = \Tools\UUID::v4();
  177. $activation_expire = date('Y-m-d H:i:s', strtotime(date('Y-m-d H:i:s'). ' + 1 days'));
  178. $statement->bindParam(':activation_token', $activation_token);
  179. $statement->bindParam(':activation_expire', $activation_expire);
  180. $password = \Tools\Crypto::getHashPassword($data['user']['password']);
  181. $statement->bindParam(':password', $password);
  182. $statement->bindParam(':firstname', $data['user']['firstname']);
  183. $statement->bindParam(':lastname', $data['user']['lastname']);
  184. $statement->bindParam(':email', $data['user']['email']);
  185. $statement->bindParam(':phone', $data['user']['phone']);
  186. // Error check
  187. if(!$statement->execute()) {
  188. $this->DataInterface->DatabaseConnection->rollback();
  189. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  190. }
  191. $fk_user = $this->DataInterface->DatabaseConnection->lastInsertId();
  192. // Insert organization
  193. $statement = $this->DataInterface->DatabaseConnection->prepare(
  194. "INSERT INTO organization(fk_user, name, fk_country, zip, city, address, phone) VALUES(:fk_user, :name, :fk_country, :zip, :city, :address, :phone)"
  195. );
  196. $statement->bindParam(':fk_user', $fk_user);
  197. $statement->bindParam(':name', $data['organization']['name']);
  198. $statement->bindParam(':fk_country', $data['organization']['country']);
  199. $statement->bindParam(':zip', $data['organization']['zip']);
  200. $statement->bindParam(':city', $data['organization']['city']);
  201. $statement->bindParam(':address', $data['organization']['address']);
  202. $statement->bindParam(':phone', $data['organization']['phone']);
  203. // Error check
  204. if(!$statement->execute()) {
  205. $this->DataInterface->DatabaseConnection->rollback();
  206. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  207. }
  208. // Insert probe
  209. $statement = $this->DataInterface->DatabaseConnection->prepare(
  210. "INSERT INTO probe(fk_user, name, brand, type, year, frequency) VALUES(:fk_user, :name, :brand, :type, :year, :frequency)"
  211. );
  212. $statement->bindParam(':fk_user', $fk_user);
  213. $statement->bindParam(':name', $data['probe']['name']);
  214. $statement->bindParam(':brand', $data['probe']['brand']);
  215. $statement->bindParam(':type', $data['probe']['type']);
  216. $statement->bindParam(':year', $data['probe']['year']);
  217. $statement->bindParam(':frequency', $data['probe']['frequency']);
  218. // Error check
  219. if(!$statement->execute()) {
  220. $this->DataInterface->DatabaseConnection->rollback();
  221. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  222. }
  223. // Commit
  224. $this->DataInterface->DatabaseConnection->commit();
  225. return [
  226. 'result' => 'OK',
  227. 'data' => $data,
  228. 'activation_token' => $activation_token,
  229. 'email' => $data['user']['email'],
  230. 'sender' => $email
  231. ];
  232. }
  233. /**
  234. * Activate an account.
  235. */
  236. public function accountActivateGet($activation_token) {
  237. $statement = $this->DataInterface->DatabaseConnection->prepare(
  238. "SELECT ID FROM user WHERE activation_token = :activation_token"
  239. );
  240. $statement->bindParam(':activation_token', $activation_token);
  241. if(!$statement->execute()) {
  242. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  243. }
  244. if(count($statement->fetchAll(\PDO::FETCH_ASSOC))==0) {
  245. return ['result' => 'ERROR', 'reason' => 'invalid_token'];
  246. }
  247. // update account
  248. $statement = $this->DataInterface->DatabaseConnection->prepare(
  249. "UPDATE user SET activation = NOW() WHERE activation_token = :activation_token"
  250. );
  251. $statement->bindParam(':activation_token', $activation_token);
  252. if(!$statement->execute()) {
  253. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  254. }
  255. $statement->bindParam(':activation_token', $activation_token);
  256. return ['result' => 'OK'];
  257. }
  258. /**
  259. * Check login/password and create JWT token.
  260. */
  261. public function accountSigninPost(&$User, $data) {
  262. if($data['type']=='physician') {
  263. $typeSql = "AND type = 'physician'";
  264. }
  265. else { // 'ct'
  266. $typeSql = "AND (type = 'reader' OR type = 'investigator')";
  267. }
  268. $statement = $this->DataInterface->DatabaseConnection->prepare(
  269. "SELECT * FROM user WHERE activation IS NOT NULL AND active = 1 AND email = :email $typeSql"
  270. );
  271. $statement->bindParam(':email', $data['email']);
  272. if(!$statement->execute()) {
  273. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  274. }
  275. $results = $statement->fetchAll(\PDO::FETCH_ASSOC);
  276. if(count($results)){
  277. if(\Tools\Crypto::verify($data['password'], $results[0]['password'])) {
  278. // Generate JWT token
  279. $issuer_claim = \Config\Settings::getTokenIssuer();
  280. $audience_claim = \Config\Settings::getTokenAudience();
  281. $issuedat_claim = time(); // issued at
  282. $notbefore_claim = $issuedat_claim + \Config\Settings::getTokenNotBefore();
  283. $expire_claim = $issuedat_claim + \Config\Settings::getTokenExpiration();
  284. $token = array(
  285. "iss" => $issuer_claim,
  286. "aud" => $audience_claim,
  287. "iat" => $issuedat_claim,
  288. "nbf" => $notbefore_claim,
  289. "exp" => $expire_claim,
  290. "data" => array(
  291. "ID" => $results[0]['ID'],
  292. "firstname" => $results[0]['firstname'],
  293. "lastname" => $results[0]['lastname'],
  294. "email" => $results[0]['email'],
  295. "type" => $results[0]['type']
  296. )
  297. );
  298. $jwt = \Firebase\JWT\JWT::encode($token, \Config\Settings::getTokenPrivateKey());
  299. // OK
  300. return [
  301. "result" => "OK",
  302. "token" => $jwt,
  303. "expireAt" => $expire_claim,
  304. "username" => $data['email'],
  305. "store" => $data['store'],
  306. "type" => $results[0]['type']
  307. ];
  308. }
  309. return ['result' => 'ERROR', 'reason' => 'bad_password', 'message' => 'Invalid password'];
  310. }
  311. return ['result' => 'ERROR', 'reason' => 'unknown', 'message' => 'No such user'];
  312. }
  313. /**
  314. * Logout.
  315. */
  316. public function accountLogoutGet(&$User) {
  317. $User->logout();
  318. return ['result' => 'OK'];
  319. }
  320. }
  321. }