Loading .gitignore +2 −0 Original line number Diff line number Diff line Loading @@ -129,6 +129,8 @@ data/templates/custom.inc.php # PHPUnit tests/.phpunit.result.cache tests/test-api.env # Residus public/js/oscar/dist/index.html Loading module/Oscar/src/Oscar/Controller/AbstractOscarController.php +2 −2 Original line number Diff line number Diff line Loading @@ -296,9 +296,9 @@ class AbstractOscarController extends AbstractActionController implements UseOsc return $this->jsonError($error); } protected function jsonError( string $msg ) :Response protected function jsonError( string $msg, $code = Response::STATUS_CODE_500 ) :Response { return $this->getResponseInternalError($msg); return $this->getHttpResponse($code, $msg); } protected function getPutDataJson() :Parameters Loading module/Oscar/src/Oscar/Controller/ApiController.php +32 −27 Original line number Diff line number Diff line Loading @@ -4,8 +4,8 @@ namespace Oscar\Controller; use BjyAuthorize\Exception\UnAuthorizedException; use Doctrine\DBAL\Driver\PDO\Exception; use Laminas\Http\Response; use Oscar\Entity\Activity; use Oscar\Entity\ActivityType; use Oscar\Entity\Person; use Oscar\Entity\ProjectPartner; use Oscar\Exception\OscarException; Loading Loading @@ -160,8 +160,10 @@ class ApiController extends AbstractOscarController implements UseOscarUserConte $strategies = json_decode($this->params()->fromPost('strategies'), JSON_OBJECT_AS_ARRAY); if( array_key_exists($login, $datas) ){ $pass_show = $datas[$login]['pass_show']; $pass_clean = $datas[$login]['pass_clean']; $hash = $datas[$login]['pass']; } else { $pass_clean = $pass; $pass_show = substr($pass,0,3) . '...' . substr($pass,strlen($pass)-3); Loading @@ -171,6 +173,7 @@ class ApiController extends AbstractOscarController implements UseOscarUserConte 'login' => $login, 'pass' => $hash, 'pass_show' => $pass_show, 'pass_clean' => $pass_clean, 'apis' => explode(',', $apis), 'strategies' => $strategies ]; Loading Loading @@ -215,22 +218,22 @@ class ApiController extends AbstractOscarController implements UseOscarUserConte if (!$authHeader) { $this->getLoggerService()->error("[OSCAR API] Missing X-API-Key header"); throw new UnAuthorizedException("[OSCAR API] Missing X-API-Key header", 401); throw new UnAuthorizedException("[OSCAR API] Missing X-API-Key header", Response::STATUS_CODE_400); } $token = $authHeader->getFieldValue(); try { $infos = $this->getOscarConfigurationService()->getApiInfos($token); } catch (\Exception $e) { $this->getLoggerService()->error("[OSCAR API] Erreur d'accès : " . $e->getMessage()); throw new OscarException("[OSCAR API] Accès interdit à l'API Oscar (Bad key)", Response::STATUS_CODE_403); } if( !in_array($api, $infos['apis']) ) { $this->getLoggerService()->error("[OSCAR API] Accès '$api' interdit"); throw new OscarException("La clef n'autorise pas '$api'"); throw new OscarException("La clef n'autorise pas l'accès : '$api'", Response::STATUS_CODE_405); } return $infos; } catch (\Exception $e) { $this->getLoggerService()->error("[OSCAR API] Erreur d'accès : " . $e->getMessage()); throw new OscarException("[OSCAR API] Accès interdit à l'API Oscar"); } } Loading Loading @@ -272,25 +275,23 @@ class ApiController extends AbstractOscarController implements UseOscarUserConte switch ($method) { case "GET": $this->checkApiAcces(self::ACCESS_PERSONS_INDEX); return $this->personsActionGet(); break; case "POST": throw new Exception("Non fait"); break; case "PUT": throw new Exception("Non fait"); throw new \Exception("Creation d'une nouvelle ressource (Non fait)", Response::STATUS_CODE_501); break; default: throw new \Exception("Indisponible", Response::STATUS_CODE_404); } } catch (\Exception $e) { return $this->jsonError($e->getMessage()); return $this->jsonError($e->getMessage(), $e->getCode()); } } die($method); protected function personsActionGet() { try { $granted = $this->checkApiAcces('persons'); $strategy = $this->getStrategy('persons'); $personToJsonFormatter = new $strategy; $personToJsonFormatter = new PersonToJsonConnectorFormatter(); $persons = []; /** @var Person $p */ Loading @@ -298,26 +299,30 @@ class ApiController extends AbstractOscarController implements UseOscarUserConte $persons[] = $personToJsonFormatter->format($p); } $datas = [ "version" => OscarVersion::getBuild(), "datecreated" => date('c'), 'time' => (microtime(true) - $start), 'total' => count($persons), 'persons' => $persons ]; $datas = $this->jsonOutputBase(); $datas['total'] = count($persons); $datas['persons'] = $persons; return $this->jsonOutput($datas); } catch (\Exception $e) { return $this->getResponseUnauthorized($e->getMessage()); return $this->jsonError($e->getMessage(), Response::STATUS_CODE_500); } } protected function jsonOutputBase() :array { return [ "version" => OscarVersion::getBuild(), "datecreated" => date('c') ]; } public function personAction() { $method = $this->getRequest()->getMethod(); $this->checkApiAcces(self::ACCESS_PERSONS_GET); switch ($method) { case "GET": $this->checkApiAcces(self::ACCESS_PERSONS_GET); $id = $this->params()->fromRoute('id'); if( $id ){ $access = self::ACCESS_PERSONS_GET; Loading tests/test-api.env.dist 0 → 100644 +10 −0 Original line number Diff line number Diff line API_BASE_URL="http://localhost:8888" ENDPOINT="/api" # Clés API (exemples) API_KEY_NOT_EXIST="JENEXISTEPAS" API_KEY_VALID="CLEF VALIDE ICI" API_KEY_NO_RIGHT="CLEF VALIDE MAIS SANS ACCESS" # Timeout curl CURL_TIMEOUT=10 No newline at end of file tests/test-api.sh 0 → 100644 +107 −0 Original line number Diff line number Diff line #!/usr/bin/env bash # ========================= # Configuration # ========================= source test-api.env # ========================= # Couleurs # ========================= RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # ========================= # Fonction de test générique # ========================= run_test() { local name="$1" local method="$2" local url="$3" local expected_code="$4" shift 4 body_tmp="$(mktemp)" echo "#########################################################################" echo -e "### TEST : ${name}" # La commande cmd=( curl -s -o "$body_tmp" -w "%{http_code}" -X "$method" "$@" "$url" ) # On affiche echo "CMD: ${cmd[*]}" # On execute response="$("${cmd[@]}")" body="$(cat "$body_tmp")" rm -f "$body_file" if [[ "$response" == "$expected_code" ]]; then echo -e "${GREEN}✔ OK ${NC} (HTTP $response)" else echo -e "${RED}✘ KO ${NC} (HTTP '$response' attendu: $expected_code)" echo "---" echo $body echo "" fi } # ========================= # Tests # ========================= run_test \ "Sans clé API (entête manquante)" \ "GET" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "400" run_test \ "La clef n'existe pas" \ "GET" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "403" \ -H "X-API-Key: JENESUISPASUNCLEFVALIDE" run_test \ "Clé valide sans droits" \ "GET" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "405" \ -H "X-API-Key: ${API_KEY_NO_RIGHT}" run_test \ "Clé valide avec droits" \ "GET" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "200" \ -H "X-API-Key: ${API_KEY_VALID}" run_test \ "Clé valide (méthode non disponible)" \ "PUT" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "404" \ -H "X-API-Key: ${API_KEY_VALID}" run_test \ "Clé valide (méthode non-faite)" \ "POST" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "501" \ -H "X-API-Key: ${API_KEY_VALID}" Loading
.gitignore +2 −0 Original line number Diff line number Diff line Loading @@ -129,6 +129,8 @@ data/templates/custom.inc.php # PHPUnit tests/.phpunit.result.cache tests/test-api.env # Residus public/js/oscar/dist/index.html Loading
module/Oscar/src/Oscar/Controller/AbstractOscarController.php +2 −2 Original line number Diff line number Diff line Loading @@ -296,9 +296,9 @@ class AbstractOscarController extends AbstractActionController implements UseOsc return $this->jsonError($error); } protected function jsonError( string $msg ) :Response protected function jsonError( string $msg, $code = Response::STATUS_CODE_500 ) :Response { return $this->getResponseInternalError($msg); return $this->getHttpResponse($code, $msg); } protected function getPutDataJson() :Parameters Loading
module/Oscar/src/Oscar/Controller/ApiController.php +32 −27 Original line number Diff line number Diff line Loading @@ -4,8 +4,8 @@ namespace Oscar\Controller; use BjyAuthorize\Exception\UnAuthorizedException; use Doctrine\DBAL\Driver\PDO\Exception; use Laminas\Http\Response; use Oscar\Entity\Activity; use Oscar\Entity\ActivityType; use Oscar\Entity\Person; use Oscar\Entity\ProjectPartner; use Oscar\Exception\OscarException; Loading Loading @@ -160,8 +160,10 @@ class ApiController extends AbstractOscarController implements UseOscarUserConte $strategies = json_decode($this->params()->fromPost('strategies'), JSON_OBJECT_AS_ARRAY); if( array_key_exists($login, $datas) ){ $pass_show = $datas[$login]['pass_show']; $pass_clean = $datas[$login]['pass_clean']; $hash = $datas[$login]['pass']; } else { $pass_clean = $pass; $pass_show = substr($pass,0,3) . '...' . substr($pass,strlen($pass)-3); Loading @@ -171,6 +173,7 @@ class ApiController extends AbstractOscarController implements UseOscarUserConte 'login' => $login, 'pass' => $hash, 'pass_show' => $pass_show, 'pass_clean' => $pass_clean, 'apis' => explode(',', $apis), 'strategies' => $strategies ]; Loading Loading @@ -215,22 +218,22 @@ class ApiController extends AbstractOscarController implements UseOscarUserConte if (!$authHeader) { $this->getLoggerService()->error("[OSCAR API] Missing X-API-Key header"); throw new UnAuthorizedException("[OSCAR API] Missing X-API-Key header", 401); throw new UnAuthorizedException("[OSCAR API] Missing X-API-Key header", Response::STATUS_CODE_400); } $token = $authHeader->getFieldValue(); try { $infos = $this->getOscarConfigurationService()->getApiInfos($token); } catch (\Exception $e) { $this->getLoggerService()->error("[OSCAR API] Erreur d'accès : " . $e->getMessage()); throw new OscarException("[OSCAR API] Accès interdit à l'API Oscar (Bad key)", Response::STATUS_CODE_403); } if( !in_array($api, $infos['apis']) ) { $this->getLoggerService()->error("[OSCAR API] Accès '$api' interdit"); throw new OscarException("La clef n'autorise pas '$api'"); throw new OscarException("La clef n'autorise pas l'accès : '$api'", Response::STATUS_CODE_405); } return $infos; } catch (\Exception $e) { $this->getLoggerService()->error("[OSCAR API] Erreur d'accès : " . $e->getMessage()); throw new OscarException("[OSCAR API] Accès interdit à l'API Oscar"); } } Loading Loading @@ -272,25 +275,23 @@ class ApiController extends AbstractOscarController implements UseOscarUserConte switch ($method) { case "GET": $this->checkApiAcces(self::ACCESS_PERSONS_INDEX); return $this->personsActionGet(); break; case "POST": throw new Exception("Non fait"); break; case "PUT": throw new Exception("Non fait"); throw new \Exception("Creation d'une nouvelle ressource (Non fait)", Response::STATUS_CODE_501); break; default: throw new \Exception("Indisponible", Response::STATUS_CODE_404); } } catch (\Exception $e) { return $this->jsonError($e->getMessage()); return $this->jsonError($e->getMessage(), $e->getCode()); } } die($method); protected function personsActionGet() { try { $granted = $this->checkApiAcces('persons'); $strategy = $this->getStrategy('persons'); $personToJsonFormatter = new $strategy; $personToJsonFormatter = new PersonToJsonConnectorFormatter(); $persons = []; /** @var Person $p */ Loading @@ -298,26 +299,30 @@ class ApiController extends AbstractOscarController implements UseOscarUserConte $persons[] = $personToJsonFormatter->format($p); } $datas = [ "version" => OscarVersion::getBuild(), "datecreated" => date('c'), 'time' => (microtime(true) - $start), 'total' => count($persons), 'persons' => $persons ]; $datas = $this->jsonOutputBase(); $datas['total'] = count($persons); $datas['persons'] = $persons; return $this->jsonOutput($datas); } catch (\Exception $e) { return $this->getResponseUnauthorized($e->getMessage()); return $this->jsonError($e->getMessage(), Response::STATUS_CODE_500); } } protected function jsonOutputBase() :array { return [ "version" => OscarVersion::getBuild(), "datecreated" => date('c') ]; } public function personAction() { $method = $this->getRequest()->getMethod(); $this->checkApiAcces(self::ACCESS_PERSONS_GET); switch ($method) { case "GET": $this->checkApiAcces(self::ACCESS_PERSONS_GET); $id = $this->params()->fromRoute('id'); if( $id ){ $access = self::ACCESS_PERSONS_GET; Loading
tests/test-api.env.dist 0 → 100644 +10 −0 Original line number Diff line number Diff line API_BASE_URL="http://localhost:8888" ENDPOINT="/api" # Clés API (exemples) API_KEY_NOT_EXIST="JENEXISTEPAS" API_KEY_VALID="CLEF VALIDE ICI" API_KEY_NO_RIGHT="CLEF VALIDE MAIS SANS ACCESS" # Timeout curl CURL_TIMEOUT=10 No newline at end of file
tests/test-api.sh 0 → 100644 +107 −0 Original line number Diff line number Diff line #!/usr/bin/env bash # ========================= # Configuration # ========================= source test-api.env # ========================= # Couleurs # ========================= RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # ========================= # Fonction de test générique # ========================= run_test() { local name="$1" local method="$2" local url="$3" local expected_code="$4" shift 4 body_tmp="$(mktemp)" echo "#########################################################################" echo -e "### TEST : ${name}" # La commande cmd=( curl -s -o "$body_tmp" -w "%{http_code}" -X "$method" "$@" "$url" ) # On affiche echo "CMD: ${cmd[*]}" # On execute response="$("${cmd[@]}")" body="$(cat "$body_tmp")" rm -f "$body_file" if [[ "$response" == "$expected_code" ]]; then echo -e "${GREEN}✔ OK ${NC} (HTTP $response)" else echo -e "${RED}✘ KO ${NC} (HTTP '$response' attendu: $expected_code)" echo "---" echo $body echo "" fi } # ========================= # Tests # ========================= run_test \ "Sans clé API (entête manquante)" \ "GET" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "400" run_test \ "La clef n'existe pas" \ "GET" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "403" \ -H "X-API-Key: JENESUISPASUNCLEFVALIDE" run_test \ "Clé valide sans droits" \ "GET" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "405" \ -H "X-API-Key: ${API_KEY_NO_RIGHT}" run_test \ "Clé valide avec droits" \ "GET" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "200" \ -H "X-API-Key: ${API_KEY_VALID}" run_test \ "Clé valide (méthode non disponible)" \ "PUT" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "404" \ -H "X-API-Key: ${API_KEY_VALID}" run_test \ "Clé valide (méthode non-faite)" \ "POST" \ "${API_BASE_URL}${ENDPOINT}/persons" \ "501" \ -H "X-API-Key: ${API_KEY_VALID}"