diff --git a/include/api.ticket.php b/include/api.ticket.php index 4fcae4b183925dfe32bb91c95275a986fc492ea6..4cb4eb02d59a8e0e9ef283abde909cc74628d4c6 100644 --- a/include/api.ticket.php +++ b/include/api.ticket.php @@ -22,7 +22,9 @@ class TicketController extends ApiController { } function create($format) { - $this->requireApiKey(); + + if(!($key=$this->getApiKey()) || !$key->canCreateTickets()) + Http::response(401, 'API key not authorized'); # Parse request body $data = $this->getRequest($format); diff --git a/include/class.api.php b/include/class.api.php index 1bd25463484a4e2f01a1d72050dde4e92d89d967..8a15c04a493c9a71cc3ae2c137ce1cc65b064fbc 100644 --- a/include/class.api.php +++ b/include/class.api.php @@ -17,102 +17,115 @@ class API { var $id; - var $info; + var $ht; - function API($id){ - $this->id=0; + function API($id) { + $this->id = 0; $this->load($id); } - function load($id) { + function load($id=0) { + + if(!$id && !($id=$this->getId())) + return false; $sql='SELECT * FROM '.API_KEY_TABLE.' WHERE id='.db_input($id); - if(($res=db_query($sql)) && db_num_rows($res)) { - $info=db_fetch_array($res); - $this->id=$info['id']; - $this->info=$info; - return true; - } - return false; + if(!($res=db_query($sql)) || !db_num_rows($res)) + return false; + + $this->ht = db_fetch_array($res); + $this->id = $this->ht['id']; + + return true; } function reload() { - return $this->load($this->getId()); + return $this->load(); } - function getId(){ + function getId() { return $this->id; } - function getKey(){ - return $this->info['apikey']; + function getKey() { + return $this->ht['apikey']; } - function getIPAddr(){ - return $this->info['ipaddr']; + function getIPAddr() { + return $this->ht['ipaddr']; } - function getNotes(){ - return $this->info['notes']; + function getNotes() { + return $this->ht['notes']; } - function isActive(){ - return ($this->info['isactive']); + function getHashtable() { + return $this->ht; } - function update($vars,&$errors){ - if(API::save($this->getId(),$vars,$errors)){ - $this->reload(); - return true; - } + function isActive() { + return ($this->ht['isactive']); + } + + function canCreateTickets() { + return ($this->ht['can_create_tickets']); + } + + function update($vars, &$errors) { + + if(!API::save($this->getId(), $vars, $errors)) + return false; - return false; + $this->reload(); + + return true; } - function delete(){ + function delete() { $sql='DELETE FROM '.API_KEY_TABLE.' WHERE id='.db_input($this->getId()).' LIMIT 1'; return (db_query($sql) && ($num=db_affected_rows())); } /** Static functions **/ - function add($vars,&$errors){ - return API::save(0,$vars,$errors); + function add($vars, &$errors) { + return API::save(0, $vars, $errors); } - function validate($key,$ip){ - - $sql='SELECT id FROM '.API_KEY_TABLE.' WHERE ipaddr='.db_input($ip).' AND apikey='.db_input($key); - return (($res=db_query($sql)) && db_num_rows($res)); + function validate($key, $ip) { + return ($key && $ip && self::getIdByKey($key, $ip)); } - function getKeyByIPAddr($ip){ + function getIdByKey($key, $ip='') { - $sql='SELECT apikey FROM '.API_KEY_TABLE.' WHERE ipaddr='.db_input($ip); + $sql='SELECT id FROM '.API_KEY_TABLE.' WHERE apikey='.db_input($key); + if($ip) + $sql.=' AND ipaddr='.db_input($ip); + if(($res=db_query($sql)) && db_num_rows($res)) - list($key)=db_fetch_row($res); + list($id) = db_insert_id(); - return $key; + return $id; } - function lookup($id){ - return ($id && is_numeric($id) && ($k= new API($id)) && $k->getId()==$id)?$k:null; + function lookupByKey($key, $ip='') { + return self::lookup(self::getIdByKey($key, $ip)); } - function save($id,$vars,&$errors){ + function lookup($id) { + return ($id && is_numeric($id) && ($k= new API($id)) && $k->getId()==$id)?$k:null; + } - if(!$id) { - if(!$vars['ipaddr'] || !Validator::is_ip($vars['ipaddr'])) - $errors['ipaddr']='Valid IP required'; - elseif(API::getKeyByIPAddr($vars['ipaddr'])) - $errors['ipaddr']='API key for the IP already exists'; - } + function save($id, $vars, &$errors) { + if(!$id && (!$vars['ipaddr'] || !Validator::is_ip($vars['ipaddr']))) + $errors['ipaddr'] = 'Valid IP required'; + if($errors) return false; - - $sql=' updated=NOW() '. - ',isactive='.db_input($vars['isactive']). - ',notes='.db_input($vars['notes']); + $sql=' updated=NOW() ' + .',isactive='.db_input($vars['isactive']) + .',can_create_tickets='.db_input($vars['can_create_tickets']) + .',notes='.db_input($vars['notes']); if($id) { $sql='UPDATE '.API_KEY_TABLE.' SET '.$sql.' WHERE id='.db_input($id); @@ -120,14 +133,17 @@ class API { return true; $errors['err']='Unable to update API key. Internal error occurred'; - }else{ - $sql='INSERT INTO '.API_KEY_TABLE.' SET '.$sql.',created=NOW() '. - ',ipaddr='.db_input($vars['ipaddr']). - ',apikey='.db_input(strtoupper(md5(time().$vars['ipaddr'].md5(Misc::randcode(16))))); + + } else { + $sql='INSERT INTO '.API_KEY_TABLE.' SET '.$sql + .',created=NOW() ' + .',ipaddr='.db_input($vars['ipaddr']) + .',apikey='.db_input(strtoupper(md5(time().$vars['ipaddr'].md5(Misc::randcode(16))))); + if(db_query($sql) && ($id=db_insert_id())) return $id; - $errors['err']='Unable to add API key. Internal error'; + $errors['err']='Unable to add API key. Try again!'; } return false; @@ -141,16 +157,24 @@ class API { * API request. */ class ApiController { + function requireApiKey() { # Validate the API key -- required to be sent via the X-API-Key # header - if (!isset($_SERVER['HTTP_X_API_KEY'])) + if (!isset($_SERVER['HTTP_X_API_KEY']) || !isset($_SERVER['REMOTE_ADDR'])) Http::response(403, "API key required"); - else if (!Api::validate($_SERVER['HTTP_X_API_KEY'], - $_SERVER['REMOTE_ADDR'])) - Http::response(401, - "API key not found or source IP not authorized"); + elseif (!($key=API::lookupByKey($_SERVER['HTTP_X_API_KEY'], $_SERVER['REMOTE_ADDR'])) + || !$key->isActive() + || $key->getIP()!=$_SERVER['REMOTE_ADDR']) + Http::response(401, "API key not found/active or source IP not authorized"); + + return $key; } + + function getApiKey() { + return $this->requireApiKey(); + } + /** * Retrieves the body of the API request and converts it to a common * hashtable. For JSON formats, this is mostly a noop, the conversion @@ -243,7 +267,7 @@ class ApiJsonDataParser extends JsonDataParser { return $current; foreach ($current as $key=>&$value) { if ($key == "phone") { - list($value,$current["phone_ext"]) + list($value, $current["phone_ext"]) = explode("X", strtoupper($value), 2); } else if ($key == "alert") { $value = (bool)$value; diff --git a/include/staff/apikey.inc.php b/include/staff/apikey.inc.php index ff5592b46fd57b374bb0a2d0825f99d3156bceef..6e2ffe6e6d8fdc4d41134726e6853162f3bfe0da 100644 --- a/include/staff/apikey.inc.php +++ b/include/staff/apikey.inc.php @@ -6,9 +6,7 @@ if($api && $_REQUEST['a']!='add'){ $title='Update API Key'; $action='update'; $submit_text='Save Changes'; - $info['id']=$api->getId(); - $info['isactive']=$api->isActive()?1:0; - $info['notes']=$api->getNotes(); + $info=$api->getHashtable(); $qstr.='&id='.$api->getId(); }else { $title='Add New API Key'; @@ -30,13 +28,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <tr> <th colspan="2"> <h4><?php echo $title; ?></h4> - <em>API Key is autogenerated and unique per IP address.</em> + <em>API Key is auto-generated. Delete and re-add to change the key.</em> </th> </tr> </thead> <tbody> <tr> - <td width="180" class="required"> + <td width="150" class="required"> Status: </td> <td> @@ -47,7 +45,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </tr> <?php if($api){ ?> <tr> - <td width="180"> + <td width="150"> IP Address: </td> <td> @@ -55,14 +53,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> </tr> <tr> - <td width="180"> + <td width="150"> API Key: </td> - <td><?php echo $api->getKey(); ?> <em>(Delete and re-add to change the key)</em></td> + <td><?php echo $api->getKey(); ?> </td> </tr> <?php }else{ ?> <tr> - <td width="180" class="required"> + <td width="150" class="required"> IP Address: </td> <td> @@ -73,16 +71,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <?php } ?> <tr> <th colspan="2"> - <em><strong>Enabled Services:</strong>: Check applicable API services.</em> + <em><strong>Enabled Services:</strong>: Check applicable API services. All active keys can make cron call.</em> </th> </tr> <tr> - <td width="180"> - Email piping: - </td> - <td> - <input type="checkbox" name="email_piping" value="1" checked="checked" disabled="disabled" > - <strong>Enable</strong> remote email piping. + <td colspan=2 style="padding-left:5px"> + <input type="checkbox" name="can_create_tickets" value="1" <?php echo $info['can_create_tickets']?'checked="checked"':''; ?> > + Can Create Tickets. <em>(XML/JSON/PIPE)</em> </td> </tr> <tr> diff --git a/include/staff/apikeys.inc.php b/include/staff/apikeys.inc.php index b085329343d101fe69de9b4628ed13ace6d6586b..90b4e31eef10af37b71ba2ebef6edffc9dc63d7b 100644 --- a/include/staff/apikeys.inc.php +++ b/include/staff/apikeys.inc.php @@ -5,7 +5,7 @@ $qstr=''; $sql='SELECT * FROM '.API_KEY_TABLE.' WHERE 1'; $sortOptions=array('key'=>'apikey','status'=>'isactive','ip'=>'ipaddr','date'=>'created','created'=>'created','updated'=>'updated'); $orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); -$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'date'; +$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'key'; //Sorting options... if($sort && $sortOptions[$sort]) { $order_column =$sortOptions[$sort]; @@ -54,10 +54,10 @@ else <thead> <tr> <th width="7"> </th> - <th width="150" nowrap><a <?php echo $date_sort; ?>href="apikeys.php?<?php echo $qstr; ?>&sort=date">Date Added</a></th> <th width="320"><a <?php echo $key_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=key">API Key</a></th> - <th width="100"><a <?php echo $status_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=status">Status</a></th> <th width="120"><a <?php echo $ip_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=ip">IP Addr.</a></th> + <th width="100"><a <?php echo $status_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=status">Status</a></th> + <th width="150" nowrap><a <?php echo $date_sort; ?>href="apikeys.php?<?php echo $qstr; ?>&sort=date">Date Added</a></th> <th width="150" nowrap><a <?php echo $updated_sort; ?>href="apikeys.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th> </tr> </thead> @@ -75,10 +75,10 @@ else <td width=7px> <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['id']; ?>" <?php echo $sel?'checked="checked"':''; ?>> </td> - <td> <?php echo Format::db_date($row['created']); ?></td> <td> <a href="apikeys.php?id=<?php echo $row['id']; ?>"><?php echo Format::htmlchars($row['apikey']); ?></a></td> - <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td> <td><?php echo $row['ipaddr']; ?></td> + <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td> + <td> <?php echo Format::db_date($row['created']); ?></td> <td> <?php echo Format::db_datetime($row['updated']); ?></td> </tr> <?php