1: <?php
2: /*****************************************************************************************
3: * X2Engine Open Source Edition is a customer relationship management program developed by
4: * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
5: *
6: * This program 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 X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
11: * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12: *
13: * This program 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 X2Engine, Inc. P.O. Box 66752, Scotts Valley,
24: * California 95067, USA. or at email address contact@x2engine.com.
25: *
26: * The interactive user interfaces in modified source and object code 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 "Powered by
32: * X2Engine" logo. If the display of the logo is not reasonably feasible for
33: * technical reasons, the Appropriate Legal Notices must display the words
34: * "Powered by X2Engine".
35: *****************************************************************************************/
36:
37: /**
38: * Password recovery active record model.
39: *
40: * @property boolean $limitReached Whether or not the requests per hour limit was reached
41: * @package application.modules.users.models
42: * @author Demitri Morgan <demitri@x2engine.com>
43: */
44: class PasswordReset extends CActiveRecord {
45:
46: /**
47: * Password reset requests expire in one hour:
48: */
49: const EXPIRE_S = 3600;
50:
51: const MAX_REQUESTS = 5;
52:
53: private $_limitReached;
54: private $_user;
55:
56: public function tableName() {
57: return 'x2_password_reset';
58: }
59:
60: public static function model($className = __CLASS__){
61: return parent::model($className);
62: }
63:
64: public function relations() {
65: return array(
66: 'user' => array(self::BELONGS_TO,'User','userId')
67: );
68: }
69:
70: public function getIpAddr(){
71: if(empty($this->ip)){
72: $this->ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
73: }
74: return $this->ip;
75: }
76:
77: public function getIsExpired() {
78: return $this->requested < time()-self::EXPIRE_S;
79: }
80:
81: /**
82: * Returns whether the maximum number of requests for the current IP address
83: * has already been reached.
84: * @return type
85: */
86: public function getLimitReached() {
87: return $this->_limitReached = (((int) self::model()->countByAttributes(array('ip'=>$this->ipAddr))) >= self::MAX_REQUESTS);
88: }
89:
90: /**
91: * Creates a password reset request.
92: *
93: * Assigns a secure/unique ID to the request.
94: * @param type $attributes
95: */
96: public function beforeSave(){
97: // Clean out old requests:
98: Yii::app()->db->createCommand('DELETE FROM `'.$this->tableName().'`'
99: . ' WHERE requested < '.(time()-self::EXPIRE_S))
100: ->execute();
101: $user = $this->resolveUser();
102: if($user instanceof User){
103: $this->userId = $user->id;
104: }
105: return !$this->limitReached && parent::beforeSave();
106: }
107:
108: public function insert($attributes = null){
109: $this->id = EncryptUtil::secureUniqueIdHash64();
110: $this->requested = time();
111: $this->getIpAddr();
112: return parent::insert($attributes);
113: }
114:
115: public function rules() {
116: return array(
117: array('email','required'),
118: array('email','email'),
119: array('email','validUserId','on'=>'afterSave'),
120: );
121: }
122:
123: /**
124: * Validator for checking if a user was found
125: * @param type $attribute
126: * @param type $params
127: */
128: public function validUserId($attribute,$params = array()) {
129: if(empty($this->userId)) {
130: $user = $this->resolveUser();
131: if($user instanceof User)
132: $this->userId = $user->id;
133: }
134: if(empty($this->userId)) {
135: $this->addError('email',Yii::t('users','No user corresponding to that email address could be found.'));
136: }
137: }
138:
139: /**
140: * Finds the user either by user or profile record (this is a sort of kludge
141: * -y safeguard that can be removed when those tables are merged)
142: * @return type
143: */
144: public function resolveUser() {
145: $user = User::model()->findByAttributes(array('emailAddress' => $this->email));
146: if(!($user instanceof User)){
147: $profile = Profile::model()->findByAttributes(array('emailAddress' => $this->email));
148: if($profile instanceof Profile) {
149: $user = $profile->user;
150: }
151: }
152: return $user;
153: }
154: }
155:
156: ?>
157: