All Data Structures Functions Variables Pages
SwitchUserIdentity.php
1 <?php
2  /*********************************************************************************
3  * Zurmo is a customer relationship management program developed by
4  * Zurmo, Inc. Copyright (C) 2017 Zurmo Inc.
5  *
6  * Zurmo is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License version 3 as published by the
8  * Free Software Foundation with the addition of the following permission added
9  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10  * IN WHICH THE COPYRIGHT IS OWNED BY ZURMO, ZURMO DISCLAIMS THE WARRANTY
11  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12  *
13  * Zurmo is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Affero General Public License along with
19  * this program; if not, see http://www.gnu.org/licenses or write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  *
23  * You can contact Zurmo, Inc. with a mailing address at 27 North Wacker Drive
24  * Suite 370 Chicago, IL 60606. or at email address contact@zurmo.com.
25  *
26  * The interactive user interfaces in original and modified versions
27  * of this program must display Appropriate Legal Notices, as required under
28  * Section 5 of the GNU Affero General Public License version 3.
29  *
30  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31  * these Appropriate Legal Notices must retain the display of the Zurmo
32  * logo and Zurmo copyright notice. If the display of the logo is not reasonably
33  * feasible for technical reasons, the Appropriate Legal Notices must display the words
34  * "Copyright Zurmo Inc. 2017. All rights reserved".
35  ********************************************************************************/
36 
41  {
42  const ERROR_NO_RIGHT_SWITCH_USER = 4;
43 
44  const PRIMARY_USER = 'primaryUser';
45 
46  const LAST_USER = 'lastUser';
47 
48  const PACKED_SESSION_KEY = 'primaryUserPackedSession';
49 
50  const PACKED_COOKIES_KEY = 'primaryUserPackedCookies';
51 
56  public function authenticate()
57  {
58  if (Yii::app()->user->userModel->username == $this->username)
59  {
60  // user is trying to switch to himself again.
61  $this->redirectForInvalidSwitchRequest();
62  }
63  if ((!static::hasPrimaryUser() && !Yii::app()->user->userModel->isSuperAdministrator()))
64  {
65  // we have an adventurous user here. He isn't admin and he hasn't got the primaryUser state
66  // set yet he wants to try.
67  return $this->noRightToSwitchUser();
68  }
69  else
70  {
71  $primaryUser = static::getPrimaryUser();
72  if ($this->username != $primaryUser)
73  {
74  $switchToUser = User::getByUsername($this->username);
75  if ($switchToUser->isRootUser)
76  {
77  // an adventurous user who is trying to switch to another Root User
78  $this->redirectForInvalidSwitchRequest();
79  }
80  }
81  if (Yii::app()->user->userModel->isSuperAdministrator() && !isset($primaryUser))
82  {
83  // we are switching from admin to someone else, store the admin
84  // we do this only for the first admin to someone else switch.
85  // if we have a a scenario like:
86  // super switches to jim
87  // jim switches to super2
88  // super2 switched to jane
89  // then we would have static::PRIMARY_USER = super as that was the actual admin.
90  $this->setPrimaryUser(Yii::app()->user->userModel->username);
91  $this->packSessionAndCookies();
92  }
93  elseif ($this->username == $primaryUser)
94  {
95  // we don't want to remember primary user anymore as we are there.
96  $this->unsetPrimaryUser();
97  $this->unpackSessionAndCookies();
98  }
99  else
100  {
101  // logout current destroy current user's session and cookies completely.
102  $this->clearSessionAndCookiesForNormalUserSwitch();
103  }
104  $this->setState('username', $this->username);
105  Yii::app()->user->setState('identityUser', $this->username);
106  Yii::app()->user->switched = true;
107  $this->errorCode = self::ERROR_NONE;
108  return true;
109  }
110  }
111 
112  protected function noRightToSwitchUser()
113  {
114  $this->errorCode = static::ERROR_NO_RIGHT_SWITCH_USER;
115  return false;
116  }
117 
118  protected function redirectForInvalidSwitchRequest()
119  {
120  Yii::app()->request->redirect(Yii::app()->homeUrl);
121  }
122 
123  protected static function hasPrimaryUser()
124  {
125  return static::doesSessionValueExist(static::PRIMARY_USER);
126  }
127 
128  public static function getPrimaryUser()
129  {
130  return static::getEncryptedSessionValue(static::PRIMARY_USER);
131  }
132 
133  protected static function setPrimaryUser($username)
134  {
135  static::setEncryptedSessionValue(static::PRIMARY_USER, $username);
136  }
137 
138  protected static function doesSessionValueExist($name)
139  {
140  return Yii::app()->session->contains($name);
141  }
142 
143  protected static function getEncryptedSessionValue($name)
144  {
145  if (static::doesSessionValueExist($name))
146  {
147  $encryptedValue = Yii::app()->session->get($name);
148  $decryptedValue = ZurmoPasswordSecurityUtil::decrypt($encryptedValue);
149  return $decryptedValue;
150  }
151  }
152 
153  protected static function setEncryptedSessionValue($name, $value)
154  {
155  // we encrypt the session value so its not so easy to read
156  $value = ZurmoPasswordSecurityUtil::encrypt($value);
157  Yii::app()->session->add($name, $value);
158  }
159 
160  protected static function unsetPrimaryUser()
161  {
162  Yii::app()->session->remove(static::PRIMARY_USER);
163  }
164 
165  protected function clearSessionAndCookiesForNormalUserSwitch()
166  {
167  $this->clearCookiesForNormalUserSwitch();
168  $this->clearSessionForNormalUserSwitch();
169  }
170 
171  protected function clearCookiesForNormalUserSwitch()
172  {
173  Yii::app()->user->logout(false);
174  Yii::app()->request->cookies->clear();
175  }
176 
177  protected function clearSessionForNormalUserSwitch()
178  {
179  $sessionKeys = Yii::app()->session->keys;
180  foreach ($sessionKeys as $sessionKey)
181  {
182  if ($this->isSessionKeyNotForForSwitchingUser($sessionKey))
183  {
184  Yii::app()->session->remove($sessionKey);
185  }
186  }
187  }
188 
189  protected function packSessionAndCookies()
190  {
191  $this->packCookies();
192  $this->packSession();
193  }
194 
195  protected function unpackSessionAndCookies()
196  {
197  // unpack session at the end so we can unpack cookies first.
198  $this->unpackCookies();
199  $this->unpackSession();
200  }
201 
202  protected function packSession()
203  {
204  $this->packIntoSession(static::PACKED_SESSION_KEY, $_SESSION);
205  }
206 
207  protected function unpackSession()
208  {
209  $this->unpackFromSession(static::PACKED_SESSION_KEY, true);
210  }
211 
212  protected function packCookies()
213  {
214  $this->packIntoSession(static::PACKED_COOKIES_KEY, $_COOKIE);
215  }
216 
217  protected function unpackCookies()
218  {
219  $this->unpackFromSession(static::PACKED_COOKIES_KEY, false);
220  }
221 
222  protected function packIntoSession($key, array $value)
223  {
224  Yii::app()->session->add($key, $this->resolveValueForPacking($value));
225  }
226 
227  protected function resolveValueForPacking(array $value)
228  {
229  return ZurmoPasswordSecurityUtil::encrypt(serialize($value));
230  }
231 
232  protected function resolveValueForUnpacking($packedValue)
233  {
234  return unserialize(ZurmoPasswordSecurityUtil::decrypt($packedValue));
235  }
236 
237  protected function unpackFromSession($key, $unpackToSession = true)
238  {
239  $packedValue = Yii::app()->session->get($key);
240  if (!empty($packedValue))
241  {
242  $unpackedValue = $this->resolveValueForUnpacking($packedValue);
243  // we can't use variable variable for super globals, neither can we pass them by reference
244  if ($unpackToSession)
245  {
246  $this->unpackSessionByValue($unpackedValue);
247  }
248  else
249  {
250  $this->unpackCookiesByValue($unpackedValue);
251  }
252  }
253  }
254 
255  protected function unpackSessionByValue(array $unpackedValue)
256  {
257  Yii::app()->session->clear();
258  foreach ($unpackedValue as $key => $value)
259  {
260  // exclude the values we used for packing user data, we can restore them but they don't
261  // serve any purpose.
262  if ($this->isSessionKeyNotForForSwitchingUser($key))
263  {
264  // we use session instead of state because not all sessions keys were part of state array
265  // also if we use state to store the value we would also have to remove the StateKeyPrefix
266  // from the keys else we would end up with 2 state key prefixes in same keys.
267  Yii::app()->session->add($key, $value);
268  }
269  }
270  }
271 
272  protected function unpackCookiesByValue(array $unpackedValue)
273  {
274  $this->clearCookiesForNormalUserSwitch();
275  foreach ($unpackedValue as $name => $value)
276  {
277  Yii::app()->request->cookies->add($name, new CHttpCookie($name, $value));
278  }
279  }
280 
281  protected function isSessionKeyNotForForSwitchingUser($key)
282  {
283  return ($key != static::PACKED_COOKIES_KEY && $key != static::PACKED_SESSION_KEY && $key !== static::PRIMARY_USER);
284  }
285  }
286 ?>
static getByUsername($username)
Definition: User.php:49
static decrypt($value, $salt=ZURMO_PASSWORD_SALT)
static encrypt($value, $salt=ZURMO_PASSWORD_SALT)
Generated on Wed Feb 26 2020 07:10:42