This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace app\controllers; |
||
4 | |||
5 | use Yii; |
||
6 | use yii\helpers\Url; |
||
7 | use yii\web\NotFoundHttpException; |
||
8 | use yii\db\Expression; |
||
9 | use app\helpers\Alert; |
||
10 | use app\models\Screen; |
||
11 | use app\models\Device; |
||
12 | use app\models\Field; |
||
13 | use app\models\Flow; |
||
14 | use app\models\Content; |
||
15 | use app\models\ContentType; |
||
16 | |||
17 | /** |
||
18 | * FrontendController implements the actions used by screens. |
||
19 | */ |
||
20 | class FrontendController extends BaseController |
||
21 | { |
||
22 | const ID = 'device_id'; |
||
23 | const EXP_YEARS = 10; |
||
24 | public $layout = 'frontend'; |
||
25 | |||
26 | /** |
||
27 | * Index redirects to associated screen ID. |
||
28 | * Checks authorization based on session & cookie |
||
29 | * Else create a new screen and display auth. |
||
30 | * |
||
31 | * @return string |
||
32 | */ |
||
33 | public function actionIndex() |
||
34 | { |
||
35 | $device = $this->getClientDevice(); |
||
36 | |||
37 | if ($device !== null) { // Associated device |
||
38 | // Check session |
||
39 | if (!$this->isClientAuth()) { |
||
40 | $this->setClientAuth($device); |
||
0 ignored issues
–
show
|
|||
41 | } |
||
42 | |||
43 | View Code Duplication | if (!$device->enabled) { |
|
44 | // Render enable view |
||
45 | return $this->render('err/authorize', [ |
||
46 | 'url' => Url::to(['device/view', 'id' => $device->id], true), |
||
47 | ]); |
||
48 | } |
||
49 | |||
50 | $screen = $device->getNextScreen(); |
||
51 | View Code Duplication | if (!$screen) { |
|
52 | // Render add screen view |
||
53 | return $this->render('err/missing-screen', [ |
||
54 | 'url' => Url::to(['device/view', 'id' => $device->id], true), |
||
55 | ]); |
||
56 | } |
||
57 | |||
58 | return $this->redirect(['screen', 'id' => $screen->id]); |
||
59 | } |
||
60 | |||
61 | // New device |
||
62 | $cookies = Yii::$app->response->cookies; |
||
63 | |||
64 | $device = new Device(); |
||
65 | $device->name = Yii::$app->request->getUserIP(); |
||
66 | $device->description = Yii::t('app', 'New unauthorized device'); |
||
67 | $device->save(); |
||
68 | $device->id = $device->lastId; |
||
69 | |||
70 | $cookies->add(new \yii\web\Cookie([ |
||
71 | 'name' => self::ID, |
||
72 | 'value' => $device->id, |
||
73 | 'expire' => time() + (self::EXP_YEARS * 365 * 24 * 60 * 60), |
||
74 | ])); |
||
75 | |||
76 | // Render enable view |
||
77 | return $this->render('err/authorize', [ |
||
78 | 'url' => Url::to(['device/view', 'id' => $device->id], true), |
||
79 | ]); |
||
80 | } |
||
81 | |||
82 | /** |
||
83 | * Initializes screen content html structure. |
||
84 | * |
||
85 | * @param int $id screen id |
||
86 | * |
||
87 | * @return \yii\web\Response|string redirect or render |
||
88 | */ |
||
89 | public function actionScreen($id, $preview = false) |
||
90 | { |
||
91 | // Session auth |
||
92 | if (!$this->isClientAuth() && !($preview && Yii::$app->user->can('previewScreen'))) { |
||
93 | return $this->redirect(['index']); |
||
94 | } |
||
95 | |||
96 | $screen = Screen::find()->where([Screen::tableName().'.id' => $id])->joinWith(['template', 'template.fields', 'template.fields.contentTypes'])->one(); |
||
97 | if ($screen === null) { |
||
98 | throw new NotFoundHttpException(Yii::t('app', 'The requested screen does not exist.')); |
||
99 | } |
||
100 | $content = [ |
||
101 | 'name' => $screen->name, |
||
102 | 'screenCss' => $screen->template->css, |
||
103 | 'background' => $screen->template->background->uri, |
||
104 | 'fields' => $screen->template->fields, |
||
105 | 'updateUrl' => $preview ? null : Url::to(['frontend/update', 'id' => $id]), |
||
106 | 'nextUrl' => Url::to(['frontend/next', 'id' => $id, 'fieldid' => '']), |
||
107 | 'types' => ContentType::getAll(), |
||
108 | ]; |
||
109 | |||
110 | return $this->render('default', $content); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * Sends last screen update timestamp, indicating if refresh is needed. |
||
115 | * |
||
116 | * @api |
||
117 | * |
||
118 | * @param int $id screen id |
||
119 | * |
||
120 | * @return string json last update |
||
121 | */ |
||
122 | public function actionUpdate($id) |
||
123 | { |
||
124 | Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; |
||
125 | // Session auth |
||
126 | if (!$this->isClientAuth()) { // Disable update if no device association |
||
127 | return ['success' => false, 'message' => 'Unauthorized']; |
||
128 | } |
||
129 | |||
130 | $screen = Screen::find()->where([Screen::tableName().'.id' => $id])->one(); |
||
131 | if ($screen === null) { |
||
132 | return ['success' => false, 'message' => 'Unknown screen']; |
||
133 | } |
||
134 | |||
135 | $device = $this->getClientDevice(); |
||
136 | $nextScreen = $device ? $device->getNextScreen($screen->id) : null; |
||
137 | |||
138 | return ['success' => true, 'data' => [ |
||
139 | 'lastChanges' => $screen->last_changes, |
||
140 | 'duration' => $screen->duration, |
||
141 | 'nextScreenUrl' => $nextScreen ? Url::to(['frontend/screen', 'id' => $nextScreen->id]) : null, |
||
142 | ]]; |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * Sends all available content for a specific field. |
||
147 | * |
||
148 | * @param int $id screen id |
||
149 | * @param int $fieldid field id |
||
150 | * |
||
151 | * @return string json array |
||
152 | */ |
||
153 | public function actionNext($id, $fieldid) |
||
154 | { |
||
155 | Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; |
||
156 | // Session auth |
||
157 | if (!$this->isClientAuth() && !Yii::$app->user->can('previewScreen')) { |
||
158 | return ['success' => false, 'message' => 'Unauthorized']; |
||
159 | } |
||
160 | |||
161 | // Get screen |
||
162 | $screen = Screen::find()->where([Screen::tableName().'.id' => $id])->joinWith(['flows'])->one(); |
||
163 | if ($screen === null) { |
||
164 | return ['success' => false, 'message' => 'Unknown screen']; |
||
165 | } |
||
166 | |||
167 | // Get field |
||
168 | $field = Field::find()->where(['id' => $fieldid])->one(); |
||
169 | if ($field === null) { |
||
170 | return ['success' => false, 'message' => 'Unknown field']; |
||
171 | } |
||
172 | |||
173 | // Get all flows for screen |
||
174 | $flows = $screen->allFlows(); |
||
175 | |||
176 | // Get all flow ids |
||
177 | $flowIds = array_map(function ($e) { |
||
178 | return $e->id; |
||
179 | }, $flows); |
||
180 | |||
181 | // Get all content type ids |
||
182 | $contentTypes = array_map(function ($e) { |
||
183 | return $e->id; |
||
184 | }, $field->contentTypes); |
||
185 | |||
186 | // Get content for flows and field type |
||
187 | $contents = Content::find() |
||
188 | ->joinWith(['flow']) |
||
189 | ->where(['type_id' => $contentTypes]) |
||
190 | ->andWhere([Flow::tableName().'.id' => $flowIds]) |
||
191 | ->andWhere(['enabled' => true]) |
||
192 | ->andWhere(['or', ['start_ts' => null], ['<', 'start_ts', new Expression('NOW()')]]) |
||
193 | ->andWhere(['or', ['end_ts' => null], ['>', 'end_ts', new Expression('NOW()')]]) |
||
194 | ->orderBy('duration ASC') |
||
195 | ->all(); |
||
196 | |||
197 | $next = array_map(function ($c) use ($field) { |
||
198 | return [ |
||
199 | 'id' => $c->id, |
||
200 | 'data' => $field->mergeData($c->getData()), |
||
201 | 'duration' => $c->duration, |
||
202 | 'type' => $c->type_id, |
||
203 | ]; |
||
204 | }, $contents); |
||
205 | |||
206 | return ['success' => true, 'next' => $next]; |
||
207 | } |
||
208 | |||
209 | /** |
||
210 | * Send an screen reload order to device. |
||
211 | * |
||
212 | * @param int $id screen id |
||
213 | * |
||
214 | * @return \yii\web\Response |
||
215 | */ |
||
216 | public function actionForceReload($id) |
||
217 | { |
||
218 | if (Yii::$app->user->can('setScreens')) { |
||
219 | $screen = Screen::findOne($id); |
||
220 | if ($screen !== null) { |
||
221 | Alert::add('Screen will reload', Alert::SUCCESS); |
||
222 | $screen->setModified(); |
||
223 | |||
224 | return $this->smartGoBack(); |
||
225 | } |
||
226 | } |
||
227 | |||
228 | Alert::add('Failed to force Screen reload', Alert::DANGER); |
||
229 | |||
230 | return $this->smartGoBack(); |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * Checks client session for device ID. |
||
235 | * |
||
236 | * @return bool is authenticated |
||
237 | */ |
||
238 | private function isClientAuth() |
||
239 | { |
||
240 | return Yii::$app->session->get(self::ID) !== null; |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * Set session with device ID, also add to DB last auth timestamp. |
||
245 | * |
||
246 | * @param \app\models\Device $device |
||
247 | */ |
||
248 | private function setClientAuth($device) |
||
249 | { |
||
250 | Yii::$app->session->set(self::ID, $device->id); |
||
251 | $device->setAuthenticated(); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Look for client device ID from session & cookie. |
||
256 | * |
||
257 | * @return int|null device ID |
||
258 | */ |
||
259 | private function getClientId() |
||
260 | { |
||
261 | $id = Yii::$app->session->get(self::ID); |
||
262 | if ($id !== null) { |
||
263 | return $id; |
||
264 | } |
||
265 | |||
266 | $cookies = Yii::$app->request->cookies; |
||
267 | $id = $cookies->getValue(self::ID); |
||
268 | if ($id !== null) { |
||
269 | return $id; |
||
270 | } |
||
271 | |||
272 | return; |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Get client device based on session & cookie. |
||
277 | * |
||
278 | * @return \app\models\Device|null device |
||
279 | */ |
||
280 | private function getClientDevice() |
||
281 | { |
||
282 | $id = $this->getClientId(); |
||
283 | if ($id === null) { |
||
284 | return; |
||
285 | } |
||
286 | |||
287 | return Device::findOne($id); |
||
288 | } |
||
289 | } |
||
290 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: