├── README.md ├── RestapiModule.php ├── components ├── CommonRest.php ├── RestApiAccessControl.php ├── RestApiUserIdentity.php └── RestUrlRule.php ├── controllers └── ApiController.php ├── migrations └── m111118_024648_create_api_users.php └── models └── ApiUsers.php /README.md: -------------------------------------------------------------------------------- 1 | How to use this module. 2 | ======================= 3 | This module is only for Yii version 1.1.8 and above. 4 | 5 | You need to load this module into the Yii modules configuration and use the RestUrlRule as one of the rules in Url 6 | Manager. 7 | 8 | In modules configuration, you must specify the modelMap configuration. If you need the module to output valid Origin for 9 | cross platform capability, include validOrigin configuration as well. 10 | 11 | e.g 12 |
13 | 'modules' => array(
14 | 'restapi' => array(
15 | 'modelMap'=>'application.restmodel',
16 | 'validOrigin'=>array(
17 | '/preg*expression/',
18 | ),
19 | ),
20 | ),
21 |
22 |
23 | - modelMap need to be in application alias format. It should point to a file that contain the mapping configuration for
24 | models.
25 |
26 | - validOrigin need to an array, it should contain a list of domain (in Regular Expression) that is allow to access this
27 | Rest API through AJAX. Check out Cross-Site HTTP request on (https://developer.mozilla.org/En/HTTP_Access_Control)
28 |
29 | Sample of the model map configuration file.
30 |
31 | array(
32 | 'ModelRestName' => array(
33 | 'class' => 'application.path.alias.ModelRestName',
34 | 'excludeAll' => true,
35 | 'exclude' => array( 'field_to_exclude', 'field_to_exclude', ),
36 | 'include' => array( 'addition_field_to_include', ),
37 | 'attributeAccessControl' => true,
38 | 'defaultCriteria' => array for CDbCriteria,
39 | ),
40 | );
41 |
42 |
43 | Add URL routing rule class in URL Manager.
44 |
45 |
46 | 'urlManager' => array(
47 | 'class'=>'CUrlManager',
48 | 'urlFormat' => 'path',
49 | 'rules' => array(
50 | array(
51 | 'class' => 'restapi.components.RestUrlRule'
52 | ),
53 | ),
54 | ),
55 |
56 |
57 | Token Access to REST API
58 | ------------------------
59 | If you are going to use Access Control in this module, you will need run migration tool from Yii.
60 |
61 | e.g
62 |
63 | php yiic migrate --migrationPath=application.modules.restapi.migrations
64 |
65 |
66 | This will create the needed table in the database to keep track of API access token, key and secret.
67 |
68 | In controller outside of restapi module that need Rest Api Access Control just add
69 | restapi.components.RestApiAccessControl as one of its filter.
70 |
71 | e.g
72 |
73 | public function filters() {
74 | return array(
75 | array(
76 | 'restapi.components.RestApiAccessControl'
77 | ),
78 | 'accessControl',
79 | );
80 | }
81 |
82 |
83 | API user will need to get temporary token from Api controller in the restapi module using their api key and secret key.
--------------------------------------------------------------------------------
/RestapiModule.php:
--------------------------------------------------------------------------------
1 |
8 | * array(
9 | * 'ModelRestName' => array(
10 | * 'class' => 'application.path.alias.ModelRestName',
11 | * 'excludeAll' => true,
12 | * 'exclude' => array( 'field_to_exclude', 'field_to_exclude', ),
13 | * 'include' => array( 'addition_field_to_include', ),
14 | * 'attributeAccessControl' => true,
15 | * 'defaultCriteria' => array for CDbCriteria,
16 | * ),
17 | * );
18 | *
19 | */
20 | public $modelMap;
21 | public $accessControl = false;
22 | public $validOrigin = array();
23 |
24 | public function checkModel($model) {
25 | if (!isset($this->modelMap[$model])) return false;
26 | return true;
27 | }
28 |
29 | public function includeModel($model) {
30 | $classname = Yii::import($this->modelMap[$model]['class']);
31 | $this->modelMap[$classname] = $this->modelMap[$model];
32 | return $classname;
33 | }
34 |
35 | public function getDefaultCriteria($model) {
36 | if (isset($this->modelMap[$model]['defaultCriteria'])) {
37 | return $this->modelMap[$model]['defaultCriteria'];
38 | } else return array();
39 | }
40 |
41 | public function getExcludedAttribute($model) {
42 | if (isset($this->modelMap[$model]['excludeAll']) && $this->modelMap[$model]['excludeAll'] === true) {
43 | $instance = new $model();
44 | $attributes = array_keys($instance->getAttributes());
45 | if (isset($this->modelMap[$model]['include'])) {
46 | return array_intersect($attributes, array_diff($attributes, $this->modelMap[$model]['include']));
47 | } else {
48 | return $attributes;
49 | }
50 | } else return isset($this->modelMap[$model]['exclude'])?$this->modelMap[$model]['exclude']:array();
51 | }
52 |
53 | public function getIncludedAttribute($model) {
54 | return isset($this->modelMap[$model]['include'])?$this->modelMap[$model]['include']:array();
55 | }
56 |
57 | public function getCheckAttributeAccessControl($model) {
58 | return (isset($this->modelMap[$model]) && isset($this->modelMap[$model]['attributeAccessControl']) && $this->modelMap[$model]['attributeAccessControl']);
59 | }
60 |
61 | protected function init() {
62 | Yii::import($this->getId().".components.*");
63 | Yii::import($this->getId().".models.*");
64 |
65 | if (empty($this->modelMap)) {
66 | Yii::log("Model Map is not set. No Model will rest.", CLogger::LEVEL_ERROR, "restapi");
67 | }
68 | if (is_string($this->modelMap)) {
69 | $this->modelMap = require(Yii::getPathOfAlias($this->modelMap).".php");
70 | }
71 | parent::init();
72 | }
73 |
74 | public function validOrigin($origin) {
75 | foreach ($this->validOrigin as $valid) {
76 | if (preg_match($valid, $origin) > 0) return true;
77 | }
78 | return false;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/components/CommonRest.php:
--------------------------------------------------------------------------------
1 | getModule("restapi")->getCheckAttributeAccessControl($model)) {
27 | $row = $list[0];
28 | /** @var CWebUser $user */
29 | $user = Yii::app()->user;
30 | foreach ($list as $rowId=>$row) {
31 | foreach ($row as $field=>$value) {
32 | if (!$user->checkAccess("view/".$_GET['model']."/".$field, array('model'=>$row), true)) {
33 | unset($list[$rowId][$field]);
34 | }
35 | }
36 | }
37 | }
38 | return $list;
39 | }
40 |
41 | /**
42 | * Get attribute of the record. Return in array
43 | *
44 | * @param CActiveRecord $record
45 | * @param string $model Model Name
46 | *
47 | * @return array of attributes
48 | */
49 | public static function getRecordAttribute($record, $model, $checkPermission = true) {
50 | /** @var $record CActiveRecord */
51 | $attributes = $record->getAttributes();
52 | if ($checkPermission) {
53 | $excludeArray = array_flip(Yii::app()->getModule("restapi")->getExcludedAttribute($model));
54 | $attributes = array_intersect_key($attributes, array_diff_key($attributes, $excludeArray));
55 | }
56 | foreach (Yii::app()->getModule("restapi")->getIncludedAttribute($model) as $includedAttribute) {
57 | try {
58 | $attributes[$includedAttribute] = $record->$includedAttribute;
59 | } catch (CException $e) {
60 | Yii::log($e->getMessage(), CLogger::LEVEL_INFO, "restapi");
61 | }
62 | }
63 | if (method_exists($record, 'attributeRestMapping')) {
64 | foreach ($record->attributeRestMapping() as $field=>$map) {
65 | if (isset($record->$field)) {
66 | $attributes[$map] = $record->$field;
67 | unset($attributes[$field]);
68 | }
69 | }
70 | }
71 | if (method_exists($record, "relations")) {
72 | foreach ($record->relations() as $name=>$relation) {
73 | try {
74 | if ($relation[0] == CActiveRecord::HAS_ONE || $relation[0] == CActiveRecord::BELONGS_TO) {
75 | if (!@class_exists($relation[1], true)) continue;
76 | /** @var $related CActiveRecord */
77 | $related = $record->$name;
78 | if ($related == null) continue;
79 | if ($related->hasAttribute("name")) $attributes[$name] = $related->name;
80 | else if ($related->hasAttribute("title")) $attributes[$name] = $related->title;
81 | else if (method_exists($related, "toString")) $attributes[$name] = $related->toString();
82 | else $attributes[$name] = $related->getPrimaryKey();
83 | }
84 | } catch (Exception $e) {
85 | Yii::log($e->getMessage(), CLogger::LEVEL_INFO, "restapi");
86 | }
87 | }
88 | }
89 | return $attributes;
90 | }
91 | }
--------------------------------------------------------------------------------
/components/RestApiAccessControl.php:
--------------------------------------------------------------------------------
1 | setToken($_GET['token']);
8 | if ($identity->authenticate()) {
9 | Yii::app()->user->login($identity);
10 | }
11 | }
12 | if (isset($_SERVER['HTTP_ORIGIN'])) {
13 | $restapi = Yii::app()->getModule("restapi");
14 | if ($restapi->validOrigin($_SERVER['HTTP_ORIGIN'])) {
15 | header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
16 | }
17 | }
18 | if ($_SERVER['REQUEST_METHOD'] == "OPTIONS") {
19 | return false;
20 | }
21 | return true;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/components/RestApiUserIdentity.php:
--------------------------------------------------------------------------------
1 | key = $key;
13 | $this->secret = $secret;
14 | }
15 |
16 | public function setToken($token) {
17 | $this->token = $token;
18 | }
19 |
20 | /**
21 | * Authenticates the user.
22 | * The information needed to authenticate the user
23 | * are usually provided in the constructor.
24 | * @return boolean whether authentication succeeds.
25 | */
26 | public function authenticate() {
27 | if ($this->token) {
28 | $this->user = ApiUsers::model()->findByAttributes(array("token"=>$this->token));
29 | if ($this->user != null && !$this->user->tokenExpired()) {
30 | $this->errorCode = self::ERROR_NONE;
31 | return true;
32 | }
33 | $this->errorMessage = "Invalid Token";
34 | } else if ($this->key && $this->secret) {
35 | /** @var $user ApiUsers */
36 | $this->user = ApiUsers::model()->findByAttributes(array("key"=>$this->key, "secret"=>$this->secret, "active"=>1));
37 | if ($this->user != null) {
38 | $this->errorCode = self::ERROR_NONE;
39 | return true;
40 | }
41 | $this->errorMessage = "Invalid Key or/and Secret";
42 | }
43 | $this->errorCode = self::ERROR_UNKNOWN_IDENTITY;
44 | return false;
45 | }
46 |
47 | /**
48 | * @return ApiUsers
49 | */
50 | public function getApiUser() {
51 | return $this->user;
52 | }
53 |
54 | public function getId() {
55 | if ($this->user) return $this->user->getPrimaryKey();
56 | else return '';
57 | }
58 |
59 | public function getName() {
60 | if ($this->user) return $this->user->username;
61 | else return '';
62 | }
63 | }
--------------------------------------------------------------------------------
/components/RestUrlRule.php:
--------------------------------------------------------------------------------
1 | value) associated with the route
10 | * @param string $ampersand the token separating name-value pairs in the URL.
11 | * @return mixed the constructed URL. False if this rule does not apply.
12 | */
13 | public function createUrl($manager, $route, $params, $ampersand) {
14 | return false;
15 | }
16 |
17 | /**
18 | * Parses a URL based on this rule.
19 | * @param CUrlManager $manager the URL manager
20 | * @param CHttpRequest $request the request object
21 | * @param string $pathInfo path info part of the URL (URL suffix is already removed based on {@link CUrlManager::urlSuffix})
22 | * @param string $rawPathInfo path info that contains the potential URL suffix
23 | * @return mixed the route that consists of the controller ID and action ID. False if this rule does not apply.
24 | */
25 | public function parseUrl($manager, $request, $pathInfo, $rawPathInfo) {
26 | $validRule = array(
27 | array('/^api\/token\/([^\/]+)\/([^\/]+)$/', 'GET', 'restapi/api/getToken',
28 | 'parameters'=>array('api'=>1, 'secret'=>2)
29 | ),
30 | array('/^api\/([a-zA-Z][a-z0-9A-Z\._\-]+)\/(.*)$/', 'GET', 'restapi/api/view',
31 | 'parameters'=>array("model"=>1, "id"=>2),
32 | ),
33 | array('/^api\/([a-zA-Z][a-z0-9A-Z\._\-]+)$/', 'GET', 'restapi/api/list',
34 | 'parameters'=>array("model"=>1)
35 | ),
36 | array('/^api\/([a-zA-Z][a-z0-9A-Z\._\-]+)\/(.*)$/', 'PUT', 'restapi/api/update',
37 | 'parameters'=>array("model"=>1, "id"=>2)
38 | ),
39 | array('/^api\/([a-zA-Z][a-z0-9A-Z\._\-]+)\/(.*)$/', 'DELETE', 'restapi/api/delete',
40 | 'parameters'=>array("model"=>1, "id"=>2)
41 | ),
42 | array('/^api\/([a-zA-Z][a-z0-9A-Z\._\-]+)$/', 'POST', 'restapi/api/create',
43 | 'parameters'=>array("model"=>1)
44 | ),
45 | );
46 | foreach ($validRule as $rule) {
47 | if ($request->getRequestType() == $rule[1]) {
48 | $matches = array();
49 | if (preg_match($rule[0], $pathInfo, $matches) >= 1) {
50 | if (isset($rule['parameters'])) {
51 | foreach ($rule['parameters'] as $param=>$index) {
52 | $_GET[$param] = $matches[$index];
53 | }
54 | return $rule[2];
55 | }
56 | }
57 | }
58 | }
59 | return false; // this rule does not apply
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/controllers/ApiController.php:
--------------------------------------------------------------------------------
1 | layout = false;
23 | Yii::app()->getErrorHandler()->errorAction = 'restapi/api/error';
24 | if (isset($_GET['model'])) {
25 | if (!$this->getModule()->checkModel($_GET['model'])) {
26 | throw new CHttpException(501, $_GET['model'].' is not implemented');
27 | } else {
28 | $_GET['model'] = $this->getModule()->includeModel($_GET['model']);
29 | }
30 | } else {
31 | if (in_array($action->getId(), array('list', 'view', 'update', 'delete', 'create'))) {
32 | throw new CHttpException(501, 'Model is not specify');
33 | }
34 | }
35 | return parent::beforeAction($action);
36 | }
37 |
38 | public function actionError() {
39 | $error = Yii::app()->getErrorHandler()->getError();
40 | Yii::log($error['trace'], CLogger::LEVEL_ERROR, "restapi");
41 | $this->renderText($error['message']);
42 | Yii::app()->end(1);
43 | }
44 |
45 | /**
46 | * Get Token for API Access.
47 | */
48 | public function actionGetToken($api, $secret) {
49 | $identity = new RestApiUserIdentity();
50 | $identity->setKeySecret($api, $secret);
51 | if ($identity->authenticate()) {
52 | $apiuser = $identity->getApiUser();
53 | if ($apiuser->tokenExpired()) {
54 | $apiuser->generateNewToken();
55 | if ($apiuser->save()) {
56 | $result = array('success'=>true, 'token'=>$apiuser->token, 'expiry'=>strtotime($apiuser->token_expire));
57 | } else {
58 | $result = array('success'=>false, 'message'=>"Can not save the generated token");
59 | }
60 | } else {
61 | $result = array('success'=>true, 'token'=>$apiuser->token, 'expiry'=>strtotime($apiuser->token_expire));
62 | }
63 | } else {
64 | $result = array('success'=>false, 'message'=>"Access Denied");
65 | }
66 | echo json_encode($result);
67 | Yii::app()->end();
68 | }
69 |
70 | /**
71 | * This method is invoked right after an action is executed.
72 | * You may override this method to do some postprocessing for the action.
73 | * @param CAction $action the action just executed.
74 | */
75 | protected function afterAction($action) {
76 | if (empty($this->result) && !is_array($this->result)) throw new CHttpException(404, "Not found with ID:".$_GET['id']);
77 | $list = CommonRest::buildModelJsonReply($this->result, $_GET['model']);
78 | header('content-type: application/json');
79 | $this->renderText(CJSON::encode(array("root"=>$list, "success"=>true)));
80 | }
81 |
82 | public function actionList($model) {
83 | if ($this->getModule()->accessControl) {
84 | /** @var CWebUser $user */
85 | $user = Yii::app()->user;
86 | if (!$user->checkAccess("list/$model", array(), true)) {
87 | throw new CHttpException(403, "Read access on $model denied.");
88 | }
89 | }
90 | $modelInstance = new $model();
91 | $limit = isset($_GET['limit'])?$_GET['limit']:50;
92 | $start = isset($_GET['start'])?$_GET['start']:0;
93 | if (isset($_GET['page'])) {
94 | $start += (($_GET['page']-1) * $limit);
95 | }
96 | if ($modelInstance instanceof CActiveRecord) {
97 | $c = new CDbCriteria($this->getModule()->getDefaultCriteria($model));
98 | $c->offset = $start;
99 | $c->limit = $limit;
100 | if (isset($_GET['filter'])) {
101 | $filter = CJSON::decode($_GET['filter']);
102 | foreach ($filter as $field=>$condition) {
103 | if (is_array($condition)) {
104 | if (is_int($field)) {
105 | $c->addCondition($condition['property'] . " = " . $condition["value"]);
106 | } else $c->addCondition("$field $condition[0] $condition[1]");
107 | } else $c->addCondition("$field = $condition");
108 | }
109 | }
110 | if (isset($_GET['sort'])) {
111 | $sort = CJSON::decode($_GET['sort']);
112 | if (is_array($sort)) {
113 | foreach ($sort as $s) {
114 | $c->order .= $s['property'] . ' ' . $s['direction'] . ',';
115 | }
116 | $c->order = substr($c->order, 0, -1);
117 | } else {
118 | $c->order = $_GET['sort'];
119 | }
120 | }
121 | if (isset($_GET['group'])) {
122 | $group = CJSON::decode($_GET['group']);
123 | if (is_array($group)) {
124 | foreach ($group as $s) {
125 | $c->group .= $s['property'] . ',';
126 | }
127 | $c->group = substr($c->group, 0, -1);
128 | } else {
129 | $c->group = $_GET['group'];
130 | }
131 | }
132 | $this->result = CActiveRecord::model($model)->findAll($c);
133 | } else if (@class_exists('EActiveResource') && $modelInstance instanceof EActiveResource) {
134 | /** @var $list EActiveResource[] */
135 | $this->result = EActiveResource::model($model)->findAll();
136 | } else if (@class_exists('EMongoDocument') && $modelInstance instanceof EMongoDocument) {
137 | $mc = new EMongoCriteria();
138 | $mc->setLimit($limit);
139 | $mc->setOffset($start);
140 | if (isset($_GET['filter'])) {
141 | $filter = CJSON::decode($_GET['filter']);
142 | foreach ($filter as $field=>$condition) {
143 | if (is_array($condition)) {
144 | if (is_int($field)) {
145 | $field = $condition['property'];
146 | $mc->$field = $condition["value"];
147 | } else $mc->$field($condition[0], $condition[1]);
148 | } else $mc->$field = $condition;
149 | }
150 | }
151 | if (isset($_GET['sort'])) $mc->setSort($_GET['sort']);
152 | $this->result = EMongoDocument::model($model)->findAll($mc);
153 | }
154 | if ($this->getModule()->accessControl) {
155 | /** @var CWebUser $user */
156 | $user = Yii::app()->user;
157 | foreach ($this->result as $key=>$item) {
158 | if (!$user->checkAccess("read/$model", array('model'=>$item), true)) {
159 | Yii::log("DENIED: Try to list model $model ID: ".$item->getPrimaryKey(), CLogger::LEVEL_INFO, "restapi");
160 | unset($this->result[$key]);
161 | }
162 | }
163 | $this->result = array_values($this->result);
164 | }
165 | }
166 |
167 | public function actionView($model, $id) {
168 | $modelInstance = new $model();
169 | if ($modelInstance instanceof CActiveRecord) {
170 | $this->result = CActiveRecord::model($model)->findByPk($id);
171 | } else if (@class_exists('EMongoDocument') && $modelInstance instanceof EMongoDocument) {
172 | $this->result = EMongoDocument::model($model)->findByPk(new MongoId($id));
173 | } else if ($modelInstance instanceof EActiveResource) {
174 | $this->result = EActiveResource::model($model)->findById($id);
175 | }
176 | if ($this->getModule()->accessControl) {
177 | /** @var CWebUser $user */
178 | $user = Yii::app()->user;
179 | if (!$user->checkAccess("view/$model", array('model'=>$this->result), true)) {
180 | throw new CHttpException(403, "Read access on $model denied.");
181 | }
182 | }
183 | }
184 |
185 | public function actionUpdate($model, $id, $type = 'update') {
186 | $this->actionView($model, $id);
187 | if (is_null($this->result)) {
188 | throw new CHttpException(400, "Did not find any model with ID: " . $id);
189 | }
190 | $modelInstance = $this->result;
191 | $modelInstance->setScenario("update");
192 | if ($this->getModule()->accessControl) {
193 | $modelInstance->setScenario($type);
194 | /** @var CWebUser $user */
195 | $user = Yii::app()->user;
196 | if ($type !== "update") $type = "update.".$type;
197 | if (!$user->checkAccess("$type/$model", array('model'=>$modelInstance, 'scenario'=>$type), true)) {
198 | throw new CHttpException(403, "Write access on $model denied.");
199 | }
200 | }
201 | $vars = CJSON::decode(file_get_contents('php://input'));
202 | if (!is_array($vars)) {
203 | Yii::log("Input need to be Json: ".var_export($vars, true), CLogger::LEVEL_ERROR, "restapi");
204 | throw new CHttpException(500, "Input need to be JSON");
205 | }
206 | if ($this->getModule()->getCheckAttributeAccessControl($_GET['model'])) {
207 | /** @var CWebUser $user */
208 | $user = Yii::app()->user;
209 | foreach ($vars as $field=>$value) {
210 | if (!$user->checkAccess("update/".$_GET['model']."/".$field, array('model'=>$modelInstance), true)) {
211 | unset($vars[$field]);
212 | Yii::log("DENIED: Try to set attribute: $field with $value", CLogger::LEVEL_INFO, "restapi");
213 | }
214 | }
215 | }
216 | $modelInstance->setAttributes($vars);
217 | if ($modelInstance->save()) {
218 | $modelInstance->refresh();
219 | } else {
220 | Yii::log(implode("\n", $modelInstance->getErrors()), CLogger::LEVEL_ERROR, "restapi");
221 | throw new CHttpException(500, "Can not save model ID: " . $id);
222 | }
223 | }
224 |
225 | public function actionDelete($model, $id) {
226 | $this->actionView($model, $id);
227 | if (is_null($this->result)) {
228 | throw new CHttpException(400, "Did not find any model with ID: " . $id);
229 | }
230 | $modelInstance = $this->result;
231 | if ($this->getModule()->accessControl) {
232 | /** @var CWebUser $user */
233 | $user = Yii::app()->user;
234 | if (!$user->checkAccess("delete/$model", array('model'=>$modelInstance), true)) {
235 | throw new CHttpException(403, "Write access on $model denied.");
236 | }
237 | }
238 | if (!$modelInstance->delete()) {
239 | Yii::log(implode("\n", $modelInstance->getErrors()), CLogger::LEVEL_ERROR, "restapi");
240 | throw new CHttpException(500, "Can not delete model ID: " . $id);
241 | } else {
242 | Yii::app()->end();
243 | }
244 | }
245 |
246 | public function actionCreate($model) {
247 | $modelInstance = new $model();
248 | $vars = CJSON::decode(file_get_contents('php://input'));
249 | if (!is_array($vars)) {
250 | Yii::log("Input need to be Json: ".var_export($vars, true), CLogger::LEVEL_ERROR, "restapi");
251 | throw new CHttpException(500, "Input need to be JSON");
252 | }
253 | if ($this->getModule()->accessControl) {
254 | /** @var CWebUser $user */
255 | $user = Yii::app()->user;
256 | if (!$user->checkAccess("create/$model", array('model'=>$modelInstance), true)) {
257 | throw new CHttpException(403, "Write access on $model denied.");
258 | }
259 | }
260 | if ($this->getModule()->getCheckAttributeAccessControl($_GET['model'])) {
261 | /** @var CWebUser $user */
262 | $user = Yii::app()->user;
263 | foreach ($vars as $field=>$value) {
264 | if (!$user->checkAccess("update/".$_GET['model']."/".$field, array('model'=>$modelInstance), true)) {
265 | unset($vars[$field]);
266 | Yii::log("DENIED: Try to set attribute: $field with $value", CLogger::LEVEL_INFO, "restapi");
267 | }
268 | }
269 | }
270 | $modelInstance->setAttributes($vars);
271 | if ($modelInstance->save()) {
272 | $modelInstance->refresh();
273 | $this->result = $modelInstance;
274 | } else {
275 | Yii::log(implode("\n", $modelInstance->getErrors()), CLogger::LEVEL_ERROR, "restapi");
276 | throw new CHttpException(500, "Can not save model: " . $model);
277 | }
278 | }
279 | }
280 |
--------------------------------------------------------------------------------
/migrations/m111118_024648_create_api_users.php:
--------------------------------------------------------------------------------
1 | createTable("{{api_users}}", array(
8 | 'id'=>'pk',
9 | 'username'=>'string NOT NULL',
10 | 'password'=>'string NOT NULL',
11 | 'key'=>'string NOT NULL',
12 | 'secret'=>'string NOT NULL',
13 | 'token'=>'string',
14 | 'token_expire'=>'datetime',
15 | 'created'=>'datetime NOT NULL',
16 | 'active'=>'boolean DEFAULT "1"'
17 | ));
18 | $this->createIndex("api_users_token_unq", "{{api_users}}", "token", true);
19 | if (strpos($this->getDbConnection()->getDriverName(), 'mysql') !== false) {
20 | $this->getDbConnection()->createCommand("ALTER TABLE {{api_users}} ENGINE=InnoDB")->execute();
21 | }
22 | $this->insert("{{api_users}}", array(
23 | "username"=>"Api Tester",
24 | "password"=>"pHuFum8tewuPregesWaWuSw9",
25 | "key"=>"hewr65ufruwaXUw5EspezA8e",
26 | "secret"=>"pRaxUBrAxEfrAwubrEsE6ARa",
27 | "token"=>"pravesTEBRUjeYecHuc4busTmUKamePapAtaWrewAzu2uPacSWUGacafadRAtRebe2RuTRav",
28 | "token_expire"=>date('Y-m-d H:i:s', time()+(60*60*24*365)),
29 | "created"=>date('Y-m-d H:i:s'),
30 | ));
31 | }
32 |
33 | public function down()
34 | {
35 | $this->dropTable("{{api_users}}");
36 | }
37 | }
--------------------------------------------------------------------------------
/models/ApiUsers.php:
--------------------------------------------------------------------------------
1 | true),
46 | array('username, password, key, secret, token', 'length', 'max'=>255),
47 | array('token_expire', 'safe'),
48 | // The following rule is used by search().
49 | // Please remove those attributes that should not be searched.
50 | array('id, username, password, key, secret, token, token_expire, created, active', 'safe', 'on'=>'search'),
51 | );
52 | }
53 |
54 | /**
55 | * @return array relational rules.
56 | */
57 | public function relations()
58 | {
59 | // NOTE: you may need to adjust the relation name and the related
60 | // class name for the relations automatically generated below.
61 | return array(
62 | );
63 | }
64 |
65 | /**
66 | * @return array customized attribute labels (name=>label)
67 | */
68 | public function attributeLabels()
69 | {
70 | return array(
71 | 'id' => 'Id',
72 | 'username' => 'Username',
73 | 'password' => 'Password',
74 | 'key' => 'Key',
75 | 'secret' => 'Secret',
76 | 'token' => 'Token',
77 | 'token_expire' => 'Token Expire',
78 | 'created' => 'Created',
79 | 'active' => 'Active',
80 | );
81 | }
82 |
83 | /**
84 | * Retrieves a list of models based on the current search/filter conditions.
85 | * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
86 | */
87 | public function search()
88 | {
89 | // Warning: Please modify the following code to remove attributes that
90 | // should not be searched.
91 |
92 | $criteria=new CDbCriteria;
93 |
94 | $criteria->compare('id',$this->id);
95 |
96 | $criteria->compare('username',$this->username,true);
97 |
98 | $criteria->compare('password',$this->password,true);
99 |
100 | $criteria->compare('key',$this->key,true);
101 |
102 | $criteria->compare('secret',$this->secret,true);
103 |
104 | $criteria->compare('token',$this->token,true);
105 |
106 | $criteria->compare('token_expire',$this->token_expire,true);
107 |
108 | $criteria->compare('created',$this->created,true);
109 |
110 | $criteria->compare('active',$this->active);
111 |
112 | return new CActiveDataProvider('ApiUsers', array(
113 | 'criteria'=>$criteria,
114 | ));
115 | }
116 |
117 | public function tokenExpired() {
118 | if ($this->token == "") return true;
119 | if (strtotime($this->token_expire) < time()) return true;
120 | return false;
121 | }
122 |
123 | public function generateNewToken() {
124 | $this->token = $this->genToken();
125 | $this->token_expire = date('Y-m-d H:i:s', strtotime("+24 hours"));
126 | }
127 |
128 | protected function genToken($len = 32) {
129 | mt_srand((double)microtime()*1000000 + time());
130 | $chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnm-_.!*)(';
131 | $numChars = strlen($chars) - 1; $token = '';
132 | for ( $i=0; $i<$len; $i++ ) {
133 | $token .= $chars[ mt_rand(0, $numChars) ];
134 | }
135 | return $token;
136 | }
137 | }
--------------------------------------------------------------------------------