diff --git a/api/controllers/Nat.php b/api/controllers/Nat.php new file mode 100644 index 0000000000000000000000000000000000000000..d220c93c8cb7e69a8941757a00f0416f10aa1a80 --- /dev/null +++ b/api/controllers/Nat.php @@ -0,0 +1,371 @@ +<?php + +/** + * phpIPAM API class to work with NAT + * + * + */ +class Nat_controller extends Common_api_functions { + /** + * _params provided + * + * @var mixed + * @access public + */ + public $_params; + + /** + * custom_fields + * + * @var mixed + * @access protected + */ + public $custom_fields; + + /** + * Database object + * + * @var mixed + * @access protected + */ + protected $Database; + + + /** + * Addresses object from master Addresses class + * + * @var mixed + * @access public + */ + public $Addresses; + + /** + * Devices objects from master Devices class + * + * @var mixed + * @access public + **/ + public $Devices; + + /** + * Response handler + * + * @var mixed + * @access protected + */ + protected $Response; + + /** + * Master Tools object + * + * @var mixed + * @access protected + */ + protected $Tools; + + /** + * valid src & dst sub-fields + **/ + protected $valid_subfields; + + /** + * Indirection using variable name to data + **/ + protected $validation_data; + + /** + * List of valid NAT types + **/ + protected $valid_types; + + /** + * __construct function + * + * @access public + * @param class $Database + * @param class $Tools + * @param mixed $params + * @param mixed $Response + */ + public function __construct($Database, $Tools, $params, $Response) { + $this->Database = $Database; + $this->Response = $Response; + $this->Tools = $Tools; + $this->_params = $params; + $this->init_object ("Admin", $Database); + $this->init_object ("Addresses", $Database); + $this->init_object ("Devices", $Database); + $this->set_valid_keys ("nat"); + $this->valid_types = array("source", "destination", "static"); + $this->validation_data = array( + "ipaddresses" => array( + "object" => $this->Addresses, + "function" => 'fetch_address', + ) + ); + } + + /** + * Returns json encoded options and version + * + * @access public + * @return void + */ + public function OPTIONS () { + // validate + $this->validate_options_request (); + + // methods + $result = array(); + $result['methods'] = array( + array("href"=>"/api/".$this->_params->app_id."/nat/", "methods"=>array(array("rel"=>"options", "method"=>"OPTIONS"))), + array("href"=>"/api/".$this->_params->app_id."/nat/{id}/", "methods"=>array(array("rel"=>"read", "method"=>"GET"), + array("rel"=>"create", "method"=>"POST"), + array("rel"=>"update", "method"=>"PATCH"), + array("rel"=>"delete", "method"=>"DELETE"))), + ); + return array("code"=>200, "data"=>$result); + } + + /** + * GET NAT functions + * + * ID can be: + * - / // returns all NATs + * - /{id}/ // returns NAT details + * + * If no ID is provided all nats are returned + * + * @access public + * @return void + */ + public function GET () { + # Get all NATs from DB + if (!isset($this->_params->id) || $this->_params->id == "all") { + $result = $this->Tools->fetch_all_objects ("nat", 'id'); + return array("code"=>200, "data"=>$this->convert_from_DB($result), true, true);#$this->prepare_result ($result, null, true, true)); + } + elseif (isset($this->_params->id) && is_numeric($this->_params->id)) { + $result = $this->Tools->fetch_multiple_objects ("nat", "id", $this->_params->id, 'id', true); + return array("code"=>200, "data"=>$this->convert_from_DB($result), true, true); + } + else { + $this->Response->throw_exception(400, "Invalid ID format"); + } + } + + /** + * Convert DB<>API format (especially for JSON-formatted fields) + * APÏ shows a valid data structure instead of JSON-formatted field + * + * @access private + * @return list of formatted results + **/ + private function convert_from_DB($results) { + $out = array(); + foreach ($results as $r) { + $rr = array( + "src" => array(), + "dst" => array() + ); + foreach ($r as $k => $v) { + if ($k != "src" && $k != "dst") { + $rr[$k] = $v; + } + else { + # Better source and destination presentation (json decode values from DB) + $jd = (array) json_decode($v, true); + if ($jd and $jd["ipaddresses"]) { + $rr[$k] = array('ipaddresses' => $jd["ipaddresses"]); + } + } + } + array_push($out, $rr); + } + return $out; + } + + /** + * HEAD, no response + * + * @access public + * @return void + */ + public function HEAD () { + return $this->GET (); + } + + /** + * Creates new NAT + * + * @access public + * @return void + */ + public function POST () { + # check for valid keys + $values = $this->validate_keys (); + # validate input format + $this->validate_nat_edit(); + foreach (array("src","dst") as $k) + $values[$k] = json_encode($values[$k]); + #$this->Response->throw_exception(500, "AAA".json_encode($values['src'])); + if (!$this->Admin->object_modify ("nat", "add", "id", $values)) { + $this->Response->throw_exception(500, "NAT creation failed"); + } + return array("code"=>201, "message"=>"NAT created", "id"=>$this->Admin->lastId, "location"=>"/api".$this->_params->app_id."/nat/".$this->Admin->lastId."/"); + } + + + /** + * Updates existing NAT + * + * @access public + * @return void + */ + public function PATCH () { + # check for valid keys + $values = $this->validate_keys (); + # validate input format + $this->validate_nat_edit(); + foreach (array("src","dst") as $k) { + if ( array_key_exists($k, $values) ) { + $values[$k] = json_encode($values[$k]); + } + } + + if (!$this->Admin->object_modify ("nat", "edit", "id", $values)) { + $this->Response->throw_exception(500, "NAT modification failed"); + } + return array("code"=>200, "message"=>"NAT modified", "id"=>$this->_params->id, "location"=>"/api".$this->_params->app_id."/nat/".$this->Admin->lastId."/"); + } + + /** + * Validates request's fields. Throw exception including error message if failing. + * + * @access private + * @return void + **/ + private function validate_nat_edit() { + if ($_SERVER['REQUEST_METHOD']=="PATCH" || $_SERVER['REQUEST_METHOD']=="DELETE") { + if (!isset($this->_params->id) || !is_numeric($this->_params->id)) { + $this->Response->throw_exception(400, "Invalid ID format"); + } + if (! $this->Tools->fetch_multiple_objects ("nat", "id", $this->_params->id, 'id', true)) { + $this->Response->throw_exception(404, "ID not found"); + } + } + if ($_SERVER['REQUEST_METHOD']=="POST" || $_SERVER['REQUEST_METHOD']=="PATCH") { + # Check optional fields format + # TBD: subnets for src & dst ? + foreach (array("src_port", "dst_port") as $k) { + if ( $this->_params->$k && !is_numeric($this->_params->$k) ) { + $this->Response->throw_exception(400, "Invalid value for $k (must be numeric)"); + } + } + foreach (array("src","dst") as $k) { + $this->verify_src_dst($k); + } + $this->verify_device(); + + } + if ($_SERVER['REQUEST_METHOD']=="POST") { + if (!isset($this->_params->name)) + $this->Response->throw_exception(400, "Missing NAT name"); # Seems to be mandatory in the GUI + if (!isset($this->_params->type)) + $this->Response->throw_exception(400, "Missing NAT type"); + elseif (!in_array($this->_params->type, $this->valid_types)) + $this->Response->throw_exception(400, "Invalid type"); + if (isset($this->_params->id) ) + $this->Response->throw_exception(400, "ID should not be set for creation"); + } + } + + /** + * Verify the device ID provided by the user is valid + * + * @access private + * @return boolean + **/ + private function verify_device() { + if (isset($this->_params->device)) { + if (is_numeric($this->_params->device)) { + $result = $this->Tools->fetch_object('devices', 'id', $this->_params->device); + if (!$result) + $this->Response->throw_exception(400, "Device ID not found"); + } + else { + $this->Response->throw_exception(400, "Wrong format for device ID (must be numeric)"); + } + } + } + + /** + * Verify user-provided "src" or "dst" parameters. Checking format is valid and the provided IDs are actually real. + * + * @access private + * @param String $k + * @return boolean + **/ + private function verify_src_dst($k) { + if ($this->_params->$k) { + #$input_param = (array) json_decode($this->_params->$k); + $input_param = $this->_params->$k; + if (!is_array($input_param)) { + $this->Response->throw_exception(400, "Invalid $k format (Must be an array"); + } + $input_out = array(); + foreach ($input_param as $pk => $pv) { + if (!in_array($pk, array_keys($this->validation_data))) { + $this->Response->throw_exception(400, "Invalid key identifier for $k ($pk)"); + } + if (!is_array($pv)) { + $this->Response->throw_exception(400, "Invalid value for $k (must be an array)"); + } + $values = array(); + foreach ($pv as $v) { + if (!is_numeric($v)) { + $this->Response->throw_exception(400, "Invalid value $v (must be an integer)"); + } + else { + # Validate IDs ! + $func = $this->validation_data[$pk]["function"]; + $object = $this->validation_data[$pk]["object"]->$func("id", $v); + if ($object) { + array_push($values, $v); + } + else { + $this->Response->throw_exception(404, "ID not found for type $pk, ID: $v"); + } + } + } + array_push($input_out, array($pk => $values)); + } + } + } + + /** + * Deletes existing NAT + * + * @access public + * @return void + */ + public function DELETE () { + $values = $this->validate_keys(); + $this->validate_nat_edit(); + if (isset($this->_params->id) && is_numeric($this->_params->id)) { + // $values = array(); + // $values['id'] = $this->_params->id; + if(!$this->Admin->object_modify ("nat", "delete", "id", $values)){ + $this->Response->throw_exception(500, "NAT delete failed"); + } + else { + return array("code"=>200, "message"=>"NAT deleted"); + } + } + else { + $this->Response->throw_exception(400, "Invalid ID format"); + } + } +} + +?>