diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php index 30deab4bfe1..ff462d4e802 100644 --- a/htdocs/contrat/class/api_contracts.class.php +++ b/htdocs/contrat/class/api_contracts.class.php @@ -21,6 +21,7 @@ use Luracast\Restler\RestException; require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; /** * API class for contracts @@ -64,14 +65,19 @@ class Contracts extends DolibarrApi * @param string $properties Restrict the data returned to these properties. Ignored if empty. Comma separated list of properties names * @param bool $withLines true or false to display or hide lines * @return Object Object with cleaned properties - * @throws RestException + * + * @throws RestException 403 Access denied + * @throws RestException 404 Not found + * @throws RestException 503 Error */ public function get($id, $properties = '', $withLines = true) { if (!DolibarrApiAccess::$user->hasRight('contrat', 'lire')) { throw new RestException(403); } - + if ($id == 0) { + throw new RestException(400, 'No contract with id=0 can exist'); + } $result = $this->contract->fetch($id); if (!$result) { throw new RestException(404, 'Contract not found'); @@ -108,7 +114,8 @@ class Contracts extends DolibarrApi * @phan-return Contrat[]|array{data:Contrat[],pagination:array{total:int,page:int,page_count:int,limit:int}} * @phpstan-return Contrat[]|array{data:Contrat[],pagination:array{total:int,page:int,page_count:int,limit:int}} * - * @throws RestException 404 Not found + * @throws RestException 400 Bad Request + * @throws RestException 403 Access denied * @throws RestException 503 Error */ public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $sqlfilters = '', $properties = '', $pagination_data = false, $withLines = true) @@ -217,6 +224,11 @@ class Contracts extends DolibarrApi * @phan-param ?array $request_data * @phpstan-param ?array $request_data * @return int ID of contrat + * + * @throws RestException 400 Bad Request + * @throws RestException 403 Access denied + * @throws RestException 404 Not found + * @throws RestException 500 Error */ public function post($request_data = null) { @@ -232,6 +244,20 @@ class Contracts extends DolibarrApi $this->contract->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09'); continue; } + if ($field == 'id') { + throw new RestException(400, 'Creating with id field is forbidden'); + } + if ($field == 'entity' && $value != getEntity('contrat')) { + throw new RestException(403, 'Creating entity not the same as your API user is forbidden'); + } + + if ($field == 'socid') { + $thirdparty = new Societe($this->db); + $result = $thirdparty->fetch((int) $value); + if ($result < 1) { + throw new RestException(404, 'Thirdparty with id='.((int) $value).' not found'); + } + } $this->contract->$field = $this->_checkValForAPI($field, $value, $this->contract); } @@ -568,10 +594,13 @@ class Contracts extends DolibarrApi * @url PUT {id}/lines/{lineid}/activate * * @return Object|bool + * + * @throws RestException 403 Access denied + * @throws RestException 404 Not found */ public function activateLine($id, $lineid, $datestart, $dateend = null, $comment = null) { - if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) { + if (!DolibarrApiAccess::$user->hasRight('contrat', 'activer')) { throw new RestException(403); } @@ -606,10 +635,13 @@ class Contracts extends DolibarrApi * @url PUT {id}/lines/{lineid}/unactivate * * @return Object|bool + * + * @throws RestException 403 Access denied + * @throws RestException 404 Not found */ public function unactivateLine($id, $lineid, $datestart, $comment = null) { - if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) { + if (!DolibarrApiAccess::$user->hasRight('contrat', 'desactiver')) { throw new RestException(403); } @@ -680,13 +712,20 @@ class Contracts extends DolibarrApi * @phan-param ?array $request_data * @phpstan-param ?array $request_data * @return Object Updated object + * + * @throws RestException 400 Bad Request + * @throws RestException 403 Access denied + * @throws RestException 404 Not found + * @throws RestException 500 Error */ public function put($id, $request_data = null) { if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) { throw new RestException(403); } - + if ($id == 0) { + throw new RestException(400, 'No contract with id=0 can exist'); + } $result = $this->contract->fetch($id); if (!$result) { throw new RestException(404, 'Contrat not found'); @@ -697,13 +736,25 @@ class Contracts extends DolibarrApi } foreach ($request_data as $field => $value) { if ($field == 'id') { - continue; + throw new RestException(400, 'Updating with id field is forbidden'); + } + if ($field == 'entity' && $value != $this->contract->entity) { + throw new RestException(400, 'Changing entity of a contract using the APIs is not possible'); } if ($field === 'caller') { // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller $this->contract->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09'); continue; } + + if ($field == 'socid') { + $thirdparty = new Societe($this->db); + $result = $thirdparty->fetch((int) $value); + if ($result < 1) { + throw new RestException(404, 'Thirdparty with id='.((int) $value).' not found'); + } + } + if ($field == 'array_options' && is_array($value)) { foreach ($value as $index => $val) { $this->contract->array_options[$index] = $this->_checkValForAPI($field, $val, $this->contract); @@ -729,12 +780,20 @@ class Contracts extends DolibarrApi * @return array * @phan-return array{success:array{code:int,message:string}} * @phpstan-return array{success:array{code:int,message:string}} + * + * @throws RestException 400 Bad Request + * @throws RestException 403 Access denied + * @throws RestException 404 Not found + * @throws RestException 500 Error */ public function delete($id) { if (!DolibarrApiAccess::$user->hasRight('contrat', 'supprimer')) { throw new RestException(403); } + if ($id == 0) { + throw new RestException(400, 'No contract with id=0 can exist'); + } $result = $this->contract->fetch($id); if (!$result) { throw new RestException(404, 'Contract not found'); @@ -774,12 +833,20 @@ class Contracts extends DolibarrApi * { * "notrigger": 0 * } + * + * @throws RestException 304 Not Modified + * @throws RestException 403 Access denied + * @throws RestException 404 Not found + * @throws RestException 500 Error */ public function validate($id, $notrigger = 0) { if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) { throw new RestException(403); } + if ($id == 0) { + throw new RestException(400, 'No contract with id=0 can exist'); + } $result = $this->contract->fetch($id); if (!$result) { throw new RestException(404, 'Contract not found'); @@ -823,12 +890,20 @@ class Contracts extends DolibarrApi * { * "notrigger": 0 * } + * + * @throws RestException 304 Not Modified + * @throws RestException 403 Access denied + * @throws RestException 404 Not found + * @throws RestException 500 Error */ public function close($id, $notrigger = 0) { if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) { throw new RestException(403); } + if ($id == 0) { + throw new RestException(400, 'No contract with id=0 can exist'); + } $result = $this->contract->fetch($id); if (!$result) { throw new RestException(404, 'Contract not found'); @@ -879,7 +954,8 @@ class Contracts extends DolibarrApi * * @param ?array $data Array with data to verify * @return array - * @throws RestException + * + * @throws RestException 400 Bad Request */ private function _validate($data) { diff --git a/test/hurl/api/contracts/10_contracts.hurl b/test/hurl/api/contracts/10_contracts.hurl new file mode 100644 index 00000000000..26ad1f5b626 --- /dev/null +++ b/test/hurl/api/contracts/10_contracts.hurl @@ -0,0 +1,112 @@ +# GET contracts +GET http://{{hostnport}}/api/index.php/contracts +HTTP 200 + +# GET sorted contracts +GET http://{{hostnport}}/api/index.php/contracts?sortfield=t.rowid&sortorder=ASC +HTTP 200 + +# GET sorted contracts +GET http://{{hostnport}}/api/index.php/contracts?sortfield=t.rowid&sortorder=DSC +HTTP 200 + +# GET with limit and page +GET http://{{hostnport}}/api/index.php/contracts?limit=100&page=1 +HTTP 200 + +# GET with fk_user +GET http://{{hostnport}}/api/index.php/contracts?fk_user=0 +HTTP 200 + +# GET with properties=id%2Cstatus +GET http://{{hostnport}}/api/index.php/contracts?properties=id%2Cstatus +HTTP 200 + +# GET with pagination_data=false +GET http://{{hostnport}}/api/index.php/contracts?pagination_data=false +HTTP 200 + +# GET with pagination_data=true +GET http://{{hostnport}}/api/index.php/contracts?pagination_data=true +HTTP 200 +[Asserts] +jsonpath "$.data" exists +jsonpath "$.pagination.total" >= 0 +jsonpath "$.pagination.page" >= 0 +jsonpath "$.pagination.page_count" >= 0 +jsonpath "$.pagination.limit" == 100 + +# GET contract with ID 0 - which should not exist +GET http://{{hostnport}}/api/index.php/contracts/0 +HTTP 400 +{"error":{"code":400,"message":"Bad Request: No contract with id=0 can exist"}} +# POST {} +POST http://{{hostnport}}/api/index.php/contracts +{} +HTTP 400 +{"error":{"code":400,"message":"Bad Request: socid field missing"}} + +# POST topic required +POST http://{{hostnport}}/api/index.php/contracts +{ "id" : 42, "socid" : "22"} +HTTP 400 +{"error":{"code":400,"message":"Bad Request: date_contrat field missing"}} + +# POST type_template field required +POST http://{{hostnport}}/api/index.php/contracts +{ "id" : 42, "socid" : "22", "date_contrat" : "873223122" } +HTTP 400 +{"error":{"code":400,"message":"Bad Request: commercial_signature_id field missing"}} + +# POST No id in post request data +POST http://{{hostnport}}/api/index.php/contracts +{ "id" : 42, "socid" : "22", "date_contrat" : "873223122", "commercial_signature_id" : "1" } +HTTP 400 +{"error":{"code":400,"message":"Bad Request: commercial_suivi_id field missing"}} + +# POST No id in post request data +POST http://{{hostnport}}/api/index.php/contracts +{ "id" : 42, "socid" : "22", "date_contrat" : "873223122", "commercial_signature_id" : "1", "commercial_suivi_id" : "1" } +HTTP 400 +{"error":{"code":400,"message":"Bad Request: Creating with id field is forbidden"}} + +# DELETE +DELETE http://{{hostnport}}/api/index.php/contracts/ +HTTP 405 + +# DELETE contract with ID 0 - which should not exist +DELETE http://{{hostnport}}/api/index.php/contracts/0 +HTTP 400 +{"error":{"code":400,"message":"Bad Request: No contract with id=0 can exist"}} + +# PUT +PUT http://{{hostnport}}/api/index.php/contracts/ +{} +HTTP 405 + +# PUT +PUT http://{{hostnport}}/api/index.php/contracts/0 +{} +HTTP 400 +{"error":{"code":400,"message":"Bad Request: No contract with id=0 can exist"}} + + + +# GET contracts DOLAPIENTITY: 1 +GET http://{{hostnport}}/api/index.php/contracts +DOLAPIENTITY: 1 +HTTP 200 + +# GET contracts DOLAPIENTITY: 2 +GET http://{{hostnport}}/api/index.php/contracts +DOLAPIENTITY: 2 +HTTP 401 +{"error":{"code":401,"message":"Unauthorized: Error user not valid (not found with api key or bad status or bad validity dates) (conf->entity=2)"}} + +# http://localhost:8080/api/index.php/contracts?sortfield=t.rowid&sortorder=ASC&limit=1 + +# Capture a contract ID number +GET http://{{hostnport}}/api/index.php/contracts?limit=1 +HTTP 200 +[Captures] +contract_id: jsonpath "$[0]['id']"