diff --git a/common/models/MobileNotification.php b/common/models/MobileNotification.php index 2c160501..5e6b41e3 100644 --- a/common/models/MobileNotification.php +++ b/common/models/MobileNotification.php @@ -1,7 +1,7 @@ params['oneSignalCandidateAPPID'])) { - return false; + if ( + empty(Yii::$app->params['inCodeception']) && + (empty(Yii::$app->params['oneSignalCandidateAPPID']) || empty(Yii::$app->params['oneSignalCandidateAPIKey'])) + ) { + Yii::warning('OneSignal candidate notification skipped because app id or API key is not configured.'); + return false; } - - self::sendNotification( - Yii::$app->params['oneSignalCandidateAPPID'], + + return self::sendNotification( + Yii::$app->params['oneSignalCandidateAPPID'], Yii::$app->params['oneSignalCandidateAPIKey'], - $heading, - $data, - $filters, - $subtitle, + $heading, + $data, + $filters, + $subtitle, $content ); } - + /** - * + * * @param string $headings * @param string $subtitle * @param string $content @@ -56,10 +60,15 @@ public static function notifyCandidate($heading, $data, $filters, $subtitle = '' * ] $filters; */ public static function sendNotification($appId, $apiKey, $heading, $data, $filters, $subtitle = '', $content = '') - { + { if(!empty(Yii::$app->params['inCodeception'])) return true; - + + if (empty($appId) || empty($apiKey)) { + Yii::warning('OneSignal notification skipped because app id or API key is not configured.'); + return false; + } + $fields = [ 'app_id' => $appId, 'filters' => $filters, @@ -87,14 +96,27 @@ public static function sendNotification($appId, $apiKey, $heading, $data, $filte curl_setopt($ch, CURLOPT_HEADER, FALSE); curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($ch, CURLOPT_TIMEOUT, 15); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE); - curl_exec($ch); + $response = curl_exec($ch); + if ($response === false) { + Yii::warning('OneSignal notification request failed: ' . curl_error($ch)); + curl_close($ch); + return false; + } + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($status < 200 || $status >= 300) { + Yii::warning('OneSignal notification request returned HTTP ' . $status . ': ' . $response); + curl_close($ch); + return false; + } curl_close($ch); /*print("\n\nJSON received:\n"); print_r($response); print("\n");*/ + return true; } } - \ No newline at end of file diff --git a/docs/demo/studenthub-55-onesignal-hardening-demo.mp4 b/docs/demo/studenthub-55-onesignal-hardening-demo.mp4 new file mode 100644 index 00000000..d7d850c9 Binary files /dev/null and b/docs/demo/studenthub-55-onesignal-hardening-demo.mp4 differ diff --git a/docs/onesignal-env.md b/docs/onesignal-env.md new file mode 100644 index 00000000..82f42ec7 --- /dev/null +++ b/docs/onesignal-env.md @@ -0,0 +1,12 @@ +# OneSignal environment configuration + +Candidate push notifications use OneSignal through `common\models\MobileNotification`. + +Set these variables in each runtime environment instead of committing OneSignal values to `params-local.php`: + +- `ONESIGNAL_CANDIDATE_APP_ID` +- `ONESIGNAL_CANDIDATE_API_KEY` + +If either variable is missing, candidate push notification sending fails closed and logs a warning. The application should still continue handling the request that attempted to enqueue the notification. + +The OneSignal API request keeps TLS peer verification enabled. Do not disable `CURLOPT_SSL_VERIFYPEER` for this integration. diff --git a/environments/circle-ci/common/config/params-local.php b/environments/circle-ci/common/config/params-local.php index 59bca7d0..e0c6996c 100644 --- a/environments/circle-ci/common/config/params-local.php +++ b/environments/circle-ci/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'test_candidate_public', 'algolia_fulltimer_index' => 'test_fulltimer_public', - 'oneSignalCandidateAPPID' => 'fe766231-6156-4537-8037-84e3fe1be5da', - 'oneSignalCandidateAPIKey' => 'YTBkODdlMjctOGQ0Ny00NDgwLTkyMmYtOWQ1NTI5ODlmZjY1', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance+fake@bawes.net', 'candidateAppUrl' => 'https://student.dev.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.dev.studenthub.co/', diff --git a/environments/dev-server-nginx-debug/common/config/params-local.php b/environments/dev-server-nginx-debug/common/config/params-local.php index 90eb554e..67c50054 100644 --- a/environments/dev-server-nginx-debug/common/config/params-local.php +++ b/environments/dev-server-nginx-debug/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'dev_candidate_public', 'algolia_fulltimer_index' => 'dev_fulltimer_public', - 'oneSignalCandidateAPPID' => 'fe766231-6156-4537-8037-84e3fe1be5da', - 'oneSignalCandidateAPIKey' => 'YTBkODdlMjctOGQ0Ny00NDgwLTkyMmYtOWQ1NTI5ODlmZjY1', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance+fake@bawes.net', 'candidateAppUrl' => 'https://student.dev.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.dev.studenthub.co/', diff --git a/environments/dev-server-nginx/common/config/params-local.php b/environments/dev-server-nginx/common/config/params-local.php index 90eb554e..67c50054 100644 --- a/environments/dev-server-nginx/common/config/params-local.php +++ b/environments/dev-server-nginx/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'dev_candidate_public', 'algolia_fulltimer_index' => 'dev_fulltimer_public', - 'oneSignalCandidateAPPID' => 'fe766231-6156-4537-8037-84e3fe1be5da', - 'oneSignalCandidateAPIKey' => 'YTBkODdlMjctOGQ0Ny00NDgwLTkyMmYtOWQ1NTI5ODlmZjY1', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance+fake@bawes.net', 'candidateAppUrl' => 'https://student.dev.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.dev.studenthub.co/', diff --git a/environments/dev-server-railway/common/config/params-local.php b/environments/dev-server-railway/common/config/params-local.php index 90eb554e..67c50054 100644 --- a/environments/dev-server-railway/common/config/params-local.php +++ b/environments/dev-server-railway/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'dev_candidate_public', 'algolia_fulltimer_index' => 'dev_fulltimer_public', - 'oneSignalCandidateAPPID' => 'fe766231-6156-4537-8037-84e3fe1be5da', - 'oneSignalCandidateAPIKey' => 'YTBkODdlMjctOGQ0Ny00NDgwLTkyMmYtOWQ1NTI5ODlmZjY1', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance+fake@bawes.net', 'candidateAppUrl' => 'https://student.dev.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.dev.studenthub.co/', diff --git a/environments/dev-server/common/config/params-local.php b/environments/dev-server/common/config/params-local.php index 90eb554e..67c50054 100644 --- a/environments/dev-server/common/config/params-local.php +++ b/environments/dev-server/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'dev_candidate_public', 'algolia_fulltimer_index' => 'dev_fulltimer_public', - 'oneSignalCandidateAPPID' => 'fe766231-6156-4537-8037-84e3fe1be5da', - 'oneSignalCandidateAPIKey' => 'YTBkODdlMjctOGQ0Ny00NDgwLTkyMmYtOWQ1NTI5ODlmZjY1', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance+fake@bawes.net', 'candidateAppUrl' => 'https://student.dev.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.dev.studenthub.co/', diff --git a/environments/dev/common/config/params-local.php b/environments/dev/common/config/params-local.php index 7ee05987..a8b0af08 100644 --- a/environments/dev/common/config/params-local.php +++ b/environments/dev/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'dev_candidate_public', 'algolia_fulltimer_index' => 'dev_fulltimer_public', - 'oneSignalCandidateAPPID' => 'fe766231-6156-4537-8037-84e3fe1be5da', - 'oneSignalCandidateAPIKey' => 'YTBkODdlMjctOGQ0Ny00NDgwLTkyMmYtOWQ1NTI5ODlmZjY1', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance+fake@bawes.net', 'candidateAppUrl' => 'https://student.dev.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.dev.studenthub.co/', diff --git a/environments/docker/common/config/params-local.php b/environments/docker/common/config/params-local.php index d621ece8..5f33210f 100644 --- a/environments/docker/common/config/params-local.php +++ b/environments/docker/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'krushn_candidate_public', 'algolia_fulltimer_index' => 'krushn_fulltimer_public', - 'oneSignalCandidateAPPID' => 'c62352ca-2f6c-44a2-896c-84c2f17db9ac', - 'oneSignalCandidateAPIKey' => 'M2E4Mjc4OWMtNGVlZi00OTRiLTkxZTAtOWU2NmM5ZDFiZmM4', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance+fake@bawes.net', 'candidateAppUrl' => 'https://student.dev.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.dev.studenthub.co/', diff --git a/environments/krushn-nginx/common/config/params-local.php b/environments/krushn-nginx/common/config/params-local.php index d621ece8..5f33210f 100644 --- a/environments/krushn-nginx/common/config/params-local.php +++ b/environments/krushn-nginx/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'krushn_candidate_public', 'algolia_fulltimer_index' => 'krushn_fulltimer_public', - 'oneSignalCandidateAPPID' => 'c62352ca-2f6c-44a2-896c-84c2f17db9ac', - 'oneSignalCandidateAPIKey' => 'M2E4Mjc4OWMtNGVlZi00OTRiLTkxZTAtOWU2NmM5ZDFiZmM4', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance+fake@bawes.net', 'candidateAppUrl' => 'https://student.dev.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.dev.studenthub.co/', diff --git a/environments/krushn/common/config/params-local.php b/environments/krushn/common/config/params-local.php index d621ece8..5f33210f 100644 --- a/environments/krushn/common/config/params-local.php +++ b/environments/krushn/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'krushn_candidate_public', 'algolia_fulltimer_index' => 'krushn_fulltimer_public', - 'oneSignalCandidateAPPID' => 'c62352ca-2f6c-44a2-896c-84c2f17db9ac', - 'oneSignalCandidateAPIKey' => 'M2E4Mjc4OWMtNGVlZi00OTRiLTkxZTAtOWU2NmM5ZDFiZmM4', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance+fake@bawes.net', 'candidateAppUrl' => 'https://student.dev.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.dev.studenthub.co/', diff --git a/environments/prod-nginx/common/config/params-local.php b/environments/prod-nginx/common/config/params-local.php index a6c37738..e6e3d0f0 100644 --- a/environments/prod-nginx/common/config/params-local.php +++ b/environments/prod-nginx/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'prod_candidate_public', 'algolia_fulltimer_index' => 'prod_fulltimer_public', - 'oneSignalCandidateAPPID' => '265d4bf5-5333-445d-8fba-08f1c389aa5f', - 'oneSignalCandidateAPIKey' => 'ZmY3OWFlMzAtN2VjNS00OWMxLTgwOWQtYjA2MDUyMzQxM2Y5', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance@bawes.net', 'candidateAppUrl' => 'https://student.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.studenthub.co/', diff --git a/environments/prod-railway/common/config/params-local.php b/environments/prod-railway/common/config/params-local.php index a6c37738..e6e3d0f0 100644 --- a/environments/prod-railway/common/config/params-local.php +++ b/environments/prod-railway/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'prod_candidate_public', 'algolia_fulltimer_index' => 'prod_fulltimer_public', - 'oneSignalCandidateAPPID' => '265d4bf5-5333-445d-8fba-08f1c389aa5f', - 'oneSignalCandidateAPIKey' => 'ZmY3OWFlMzAtN2VjNS00OWMxLTgwOWQtYjA2MDUyMzQxM2Y5', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance@bawes.net', 'candidateAppUrl' => 'https://student.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.studenthub.co/', diff --git a/environments/prod/common/config/params-local.php b/environments/prod/common/config/params-local.php index a6c37738..e6e3d0f0 100644 --- a/environments/prod/common/config/params-local.php +++ b/environments/prod/common/config/params-local.php @@ -6,8 +6,8 @@ ], 'algolia_candidate_index' => 'prod_candidate_public', 'algolia_fulltimer_index' => 'prod_fulltimer_public', - 'oneSignalCandidateAPPID' => '265d4bf5-5333-445d-8fba-08f1c389aa5f', - 'oneSignalCandidateAPIKey' => 'ZmY3OWFlMzAtN2VjNS00OWMxLTgwOWQtYjA2MDUyMzQxM2Y5', + 'oneSignalCandidateAPPID' => getenv('ONESIGNAL_CANDIDATE_APP_ID') ?: '', + 'oneSignalCandidateAPIKey' => getenv('ONESIGNAL_CANDIDATE_API_KEY') ?: '', 'finance_transfer' => 'finance@bawes.net', 'candidateAppUrl' => 'https://student.studenthub.co/', 'inspectorAppUrl' => 'https://inspector.studenthub.co/', diff --git a/tests/check-onesignal-env-config.sh b/tests/check-onesignal-env-config.sh new file mode 100755 index 00000000..6ed6580c --- /dev/null +++ b/tests/check-onesignal-env-config.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +if rg -n "oneSignalCandidate(APPID|APIKey)'\\s*=>\\s*'[A-Za-z0-9-]{20,}'" environments --glob 'params-local.php'; then + echo "OneSignal candidate credentials must come from runtime environment variables." >&2 + exit 1 +fi + +while IFS= read -r params_file; do + rg -n "getenv\\('ONESIGNAL_CANDIDATE_APP_ID'\\)" "$params_file" >/dev/null || { + echo "$params_file must read ONESIGNAL_CANDIDATE_APP_ID from the runtime environment." >&2 + exit 1 + } + rg -n "getenv\\('ONESIGNAL_CANDIDATE_API_KEY'\\)" "$params_file" >/dev/null || { + echo "$params_file must read ONESIGNAL_CANDIDATE_API_KEY from the runtime environment." >&2 + exit 1 + } +done < <(find environments -path '*/common/config/params-local.php' -type f | sort) + +if rg -n "CURLOPT_SSL_VERIFYPEER,\\s*[Ff][Aa][Ll][Ss][Ee]" common/models/MobileNotification.php; then + echo "OneSignal requests must not disable TLS peer verification." >&2 + exit 1 +fi + +rg -n "CURLOPT_CONNECTTIMEOUT" common/models/MobileNotification.php >/dev/null +rg -n "CURLOPT_TIMEOUT" common/models/MobileNotification.php >/dev/null +rg -n "CURLINFO_HTTP_CODE" common/models/MobileNotification.php >/dev/null + +echo "OneSignal notification config uses runtime env vars and keeps TLS verification enabled."