jf-guillou /
lcds
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: