Odyssey
UserLogin.php
1 <?php
2 
3 /**
4  * PHP User Login Class
5  *
6  * Handles username and password validation, challenge questions, etc.
7  *
8  * Written: 10/2019
9  *
10  */
11 
12 class UserLogin {
13 
14  //the CU to use
15  private $cu = NULL;
16 
17  //a string holding the cookie prefix
18  private $cookie_prefix = '';
19 
20  //user is new
21  public $new_user = false;
22 
23  //array to hold all of the errors
24  public $errors = NULL;
25 
26  /*
27  * Class Constructor
28  *
29  * user - User object
30  * cuadmin - CuAdmin object
31  * cookie_prefix - str - unique cookie prefix
32  */
33  function __construct(User $user, CuAdmin $cuAdmin, $cookie_prefix = 'usr'){
34 
35  if (!$user instanceof User) {
36  throw new InvalidArgumentException(static::class . ': User object required.');
37  }
38  if (!$cuAdmin instanceof CuAdmin) {
39  throw new InvalidArgumentException(static::class . ': CuAdmin object required.');
40  }
41 
42  $this->user = $user;
43  $this->cuAdmin = $cuAdmin;
44  }
45 
46  /*
47  * IsAccountLocked
48  *
49  * Check to see if the member's account is locked
50  *
51  * @return bool
52  */
53  public function IsAccountLocked() {
54  if (!$this->user->isUserLoaded) {
55  throw new Exception(static::class . ': User is unvalidated.');
56  }
57  switch (true) {
58  // Challenge questions need setup
59  case (boolval($this->cuAdmin->admininfo['flagset3'] & CuAdmin::CU3_MFA_AUTHCODE) == 0 && ($this->user->data['freset'] === User::MEM_FORCE_RESET || $this->user->mfaquest['mfacount'] < $this->cuAdmin->admininfo['min_chlng_qst'])):
60  $this->new_user = true;
61  $forceUpdate = true;
62  break;
63  // Password
64  case $this->user->data['fchange'] === 'Y':
65  $forceUpdate = true;
66  break;
67  // Member Reset Password
68  case (boolval($this->cuAdmin->admininfo['flagset'] & CuAdmin::CU_MEMRESET)):
69  $forceUpdate = true;
70  break;
71  // Force Reset Password
72  case $this->user->data['userflags'] & User::MEM_FORCE_RESET:
73  $forceUpdate = true;
74  break;
75  // Phone numbers
76  case (boolval($this->cuAdmin->admininfo['flagset3'] & CuAdmin::CU3_MFA_AUTHCODE > 0
77  ) && ($this->user->data['freset'] === 2)):
78  $forceUpdate = true;
79  break;
80  // User alias
81  case $this->cuAdmin->admininfo['flagset2'] & CuAdmin::CU2_ALIAS_REQ && !preg_match("/\D/",$this->user->user_name):
82  $forceUpdate = true;
83  break;
84  default:
85  $forceUpdate = false;
86  }
87  return $this->user->data['failedremain'] <= 0 || ($forceUpdate && $this->user->data['fremain'] <= 0);
88 
89  }
90 
91  /*
92  * IsValidDeviceCookie
93  *
94  * Check to see if the member's device cookie is valid
95  * $cu - Credit Union Code
96  * @return bool
97  */
98  public function IsValidDeviceCookie($cu) {
99  $cookiename = Return2FactorName($cu, Get2FactorKeyString(), $this->user->user_name);
100  $mfaMode = intval($this->cuAdmin->admininfo['flagset3'] & CuAdmin::CU3_MFA_AUTHCODE);
101 
102  // Check if user has confidence word or not
103  $mfaDate = $this->user->mfaquest['mfadate'];
104 
105  if ($this->user->data['confidence'] != '') {
106  $cookiecontent = hash_hmac('sha384', GetDeviceCookieContentString(),trim($this->user->data['passwd']) . trim(strtolower($this->user->data['email'])) . trim(strtolower($this->user->data['confidence'])) . $mfaMode . $mfaDate);
107  } else {
108  $cookiecontent = hash_hmac('sha384', GetDeviceCookieContentString(),trim($this->user->data['passwd']) . trim(strtolower($this->user->data['email'])) . $mfaMode . $mfaDate);
109  }
110 
111  if (!empty($_COOKIE[$cookiename]) && $cookiecontent == $_COOKIE[$cookiename] && $this->user->data['freset'] != User::MEM_FORCE_RESET) {
112  $return_val = true;
113  } else {
114  $return_val = false;
115  }
116  return $return_val;
117  }
118 
119  /*
120  * IsValidMammothDeviceCookie
121  *
122  * Check to see if the member's old Mammoth device cookie is valid
123  * $cu - Credit Union Code
124  * @return bool
125  */
126  public function IsValidMammothDeviceCookie($cu, $hbenv) {
127  $return_val = false; // assume not valid
128  // check if allowed
129  if (($this->cuAdmin->admininfo['flagset3'] & CuAdmin::CU3_ALLOW_COOKIE_MIGRATION) > 0 &&
130  $this->user->data['freset'] != User::MEM_FORCE_RESET) {
131  // get the possible account numbers
132 
133  $username = $this->user->data['user_name'];
134  $sql = "SELECT DISTINCT ua.accountnumber
135  FROM {$cu}user u
136  INNER JOIN {$cu}useraccounts ua on ua.user_id = u.user_id
137  WHERE u.user_name = '{$username}'";
138 
139  $rs = db_query($sql, $hbenv["dbh"]);
140 
141  $row = 0;
142  while ($aRow = db_fetch_array($rs, $row++)) {
143  $thisAccount = $aRow["accountnumber"];
144  $mammothCookieName = Return2FactorName($cu, $hbenv['2factorkey'], trim($thisAccount));
145 
146  if (isset($_COOKIE[$mammothCookieName])) {
147  // check the contents
148  $mammothCookieContent = sha1(trim($this->user->data['passwd']) . trim($this->user->data['email']) . trim($this->user->data['confidence']));
149 
150  $return_val = $mammothCookieContent == $_COOKIE[$mammothCookieName];
151 
152  if ($return_val) {
153  // update the cookie for odyssey
154  $this->UpdateMammothDeviceCookie($cu, $hbenv, $mammothCookieName);
155  }
156  // exit the loop
157  break;
158  }
159  }
160  }
161  return $return_val;
162  }
163 
164  /*
165  * UpdateMammothDeviceCookie
166  *
167  *
168  */
169  private function UpdateMammothDeviceCookie($cu, $hbenv, $mammothCookieName){
170  // we need to set up some variables the new device cookie will need
171  $hbenv["cu"] = $cu;
172  $hbenv['confidence'] = trim($this->user->data['confidence']);
173  $hbenv["Cn"] = $this->user->data['user_name'];
174  $hbenv["Fset3"] = $this->cuAdmin->admininfo['flagset3'];
175  $hbenv["savepass"] = $this->user->data["passwd"];
176  $hbenv["savemail"] = $this->user->data["email"];
177 
178  // remove the old one first in case the cookie names are the same
179  $inThePast = time() - 3600 * 24;
180  HCU_setcookie_env($hbenv['SYSENV'], $mammothCookieName, "", $inThePast);
181 
182  // replace the old device cookie with a new one
183  $this->SetLoginDeviceCookie($hbenv);
184  }
185 
186  /*
187  * Function: SetLoginDeviceCookie
188  * Purpose: To manage the 2Factor Device cookie -- At this time it is Browser
189  * based
190  *
191  * @param array hbenv -- The current HB_ENV value
192  * @return false
193  */
194 function SetLoginDeviceCookie($hbenv) {
195 
196  $now = time();
197 
198  if (sizeof($_COOKIE) > 6
199  && !preg_match("/^199.184.207/",$_SERVER['REMOTE_ADDR'])
200  && !preg_match("/^192.168/",$_SERVER['REMOTE_ADDR'])) {
201 
202  $emsg = "{$_SERVER['REMOTE_ADDR']} {$hbenv['cu']}:{$hbenv['Cn']} " . date('Y-m-d H:i:s') . " " . sizeof($_COOKIE) . " Cookies";
203  $hbenv['SYSENV']['logger']->warning($emsg);
204  }
205 
206  // ** Make sure the COOKIES ARE KEPT TO MINIMUM FOR the browser, these add to
207  // * size of packet being sent back to server from client
208 
209  if (sizeof($_COOKIE) > 23) {
210  reset ($_COOKIE);
211  $persists = ($now - 3600);
212  foreach ($_COOKIE as $cookiename => $cookiecontent) {
213  if (!preg_match("/^(Tx_mURI|Ticket|webconnect)/",$cookiename)) {
214  HCU_setcookie_env($hbenv['SYSENV'], $cookiename, "", $persists);
215  }
216  }
217  }
218 
219  $cookiename = Return2FactorName($hbenv["cu"], Get2FactorKeyString(), $this->user->user_name);
220  $mfaMode = intval($this->cuAdmin->admininfo['flagset3'] & CuAdmin::CU3_MFA_AUTHCODE);
221 
222  // Check if user has confidence word or not
223  $mfaDate = $this->user->mfaquest['mfadate'];
224 
225  if ($this->user->data['confidence'] != '') {
226  $cookiecontent = hash_hmac('sha384', GetDeviceCookieContentString(),trim($this->user->data['passwd']) . trim(strtolower($this->user->data['email'])) . trim(strtolower($this->user->data['confidence'])) . $mfaMode . $mfaDate);
227  } else {
228  $cookiecontent = hash_hmac('sha384', GetDeviceCookieContentString(),trim($this->user->data['passwd']) . trim(strtolower($this->user->data['email'])) . $mfaMode . $mfaDate);
229  }
230 
231  $persists = $now + $hbenv['SYSENV']['ticket']['persists'];
232 
233  HCU_setcookie_env($hbenv['SYSENV'], $cookiename, $cookiecontent, $persists);
234 }
235 
236  /**
237  * CreateSessionTicket
238  *
239  */
240  public function CreateSessionTicket($hbenv, $mc, $cu) {
241  $hbenv['cu'] = $cu;
242  $hbenv['platform'] = $hbenv['platform'];
243  $hbenv['Uid'] = $this->user->data['user_id'];
244  $hbenv['Cn'] = $this->user->data['user_name'];
245  $hbenv['Ce'] = time() + $hbenv['SYSENV']['ticket']['expires'];
246  $hbenv['Clw'] = $this->cuAdmin->admininfo['livewait'];
247  $hbenv['Clu'] = (empty($this->user->data['lastupdate']) ? $mc->msg("Unknown") : urlencode(trim($this->user->data['lastupdate'])));
248  $hbenv['lastupdate'] = (empty($this->user->data['lastupdate']) ? "Unknown" : urlencode(trim($this->user->data['lastupdate'])));
249  $hbenv['Fplog'] = (empty($this->user->data['llog']) ? $mc->msg("None") : urlencode(trim($this->user->data['llog'])));
250  $hbenv['Fflog'] = (empty($this->user->data['flog']) ? $mc->msg("None") : urlencode(trim($this->user->data['flog'])));
251  $hbenv['Ffchg'] = (is_null($this->user->data['fchange']) ? 'N' : $this->user->data['fchange']);
252  $hbenv['Ffremain'] = (is_null($this->user->data['fremain']) || $this->user->data['fremain'] == 0 ? $this->cuAdmin->admininfo['grace'] : $this->user->data['fremain']);
253  $hbenv['Fmsg_tx'] = $this->user->data['msg_tx'];
254  $hbenv['Fset'] = $this->cuAdmin->admininfo['flagset'];
255  $hbenv['Fset2'] = $this->cuAdmin->admininfo['flagset2'];
256  $hbenv['Fset3'] = $this->cuAdmin->admininfo['flagset3'];
257  $hbenv['Fhdays'] = $this->cuAdmin->admininfo['fhdays'];
258  $hbenv['Ml'] = $this->user->data['email'];
259  $hbenv['Ffreset'] = $this->user->data['freset'];
260 
261  // * Add this for use later possibly in 2Factor cookie
262  $hbenv['savepass'] = $this->user->data['passwd'];
263  $hbenv['savemail'] = $this->user->data['email'];
264 
265  // Set the sid at this point. It is not updated from this point.
266  $hbenv["sid"] = strval(time());
267  // SET THE MEMBER COOKIE
268  $now = time();
269 
270  $baseCookie = BuildBaseSessionTicket($hbenv);
271  $mycookie = "Ctime=$now&Cn={$this->user->data['user_name']}&Uid={$this->user->data['user_id']}&Ml=" . urlencode($this->user->data['email']) . "&Ca=";
272 
273  // ** Check for testmenu param -- If set to 1, then I want to add it to cookie
274  // if (intval(HCU_array_key_value('testmenu', $hbenv['HCUPOST'])) == 1) {
275  // $mycookie .= "&testmenu=1";
276  // }
277  // ** SET THE COOKIE
278  SetTicket($hbenv, $baseCookie, $mycookie);
279  }
280 
281  /*
282  * ValidateUser
283  *
284  * Verify the username and password against the database
285  * and log the user in if successful.
286  *
287  * Arguments:
288  * password - str - The user's password (only passing raw currently)
289  * remember_me - bool - Whether to remember this user or not (not using yet)
290  *
291  */
292  function ValidateUser($password = '', $remember_me = false) {
293  if (empty($password)) {
294  throw new InvalidArgumentException(static::class . ': Invalid password.');
295  }
296  // if (!password_verify($password, $this->user->data['passwd'])) {
297  // throw new InvalidArgumentException(static::class . ': Invalid password.');
298  // }
299  return password_verify($password, $this->user->data['passwd']);
300  }
301 
302  /*
303  * IsMFAMode
304  *
305  * @return bool
306  *
307  */
308  function IsMFAMode() {
309  return boolval($this->cuAdmin->admininfo['flagset3'] & CuAdmin::CU3_MFA_AUTHCODE);
310  }
311 
312 
313 }
314 
CreateSessionTicket($hbenv, $mc, $cu)
Definition: UserLogin.php:240
Definition: User.php:7