public static function validatePath(RecordValidator $validator, Endpoint $Endpoint) { if (!$Endpoint->Path) { $validator->addError('Path', 'Path must not be empty'); return; } if ($Endpoint->Path[0] == '/') { $validator->addError('Path', 'Path must not start with /'); return; } if (substr($Endpoint->Path, -1) == '/') { $validator->addError('Path', 'Path must not end with /'); return; } if (!preg_match(static::$validPathRegex, $Endpoint->Path)) { $validator->addError('Path', 'Path must start with a letter and only contain letters, numbers, periods, hyphens, and underscores'); return; } $duplicateConditions = ['Path' => $Endpoint->Path]; if ($Endpoint->ID) { $duplicateConditions[] = 'ID != ' . $Endpoint->ID; } if (Endpoint::getByWhere($duplicateConditions)) { $validator->addError('Path', 'Path matches an existing endpoint\'s'); return; } }
public static function handleBrowseRequest($options = [], $conditions = [], $responseID = null, $responseData = []) { // apply status filter if (empty($_GET['status'])) { $status = 'open'; } elseif ($_GET['status'] == 'any') { $status = null; } elseif (in_array($_GET['status'], AbstractAlert::getFieldOptions('Status', 'values'))) { $status = $_GET['status']; } else { $status = 'open'; } if ($status) { $responseData['status'] = $conditions['Status'] = $status; } // apply endpoint filter if (!empty($_GET['endpoint'])) { if (!($Endpoint = Endpoint::getByHandle($_GET['endpoint']))) { return static::throwNotFoundError('Endpoint not found'); } } if (isset($Endpoint)) { $conditions['EndpointID'] = $Endpoint->ID; $responseData['Endpoint'] = $Endpoint; } return parent::handleBrowseRequest($options, $conditions, $responseID, $responseData); }
public function getUnlinkedEndpoints() { $currentEndpoints = array_map(function ($Endpoint) { return $Endpoint->ID; }, $this->Endpoints); return count($currentEndpoints) ? Endpoint::getAllByWhere('ID NOT IN (' . implode(',', $currentEndpoints) . ')') : Endpoint::getAll(); }
public static function handleEndpointsCurrentRequest() { $GLOBALS['Session']->requireAccountLevel('Staff'); $results = []; foreach (Endpoint::getAll() as $Endpoint) { $results[] = ['EndpointID' => $Endpoint->ID, 'requests' => $Endpoint->getCounterMetric('requests'), 'responseTime' => $Endpoint->getAverageMetric('responseTime', 'requests'), 'responsesExecuted' => $Endpoint->getCounterMetric('responsesExecuted'), 'responsesCached' => $Endpoint->getCounterMetric('responsesCached'), 'bytesExecuted' => $Endpoint->getCounterMetric('bytesExecuted'), 'bytesCached' => $Endpoint->getCounterMetric('bytesCached')]; } return static::respond('currentEndpointMetrics', ['data' => $results]); }
public static function handleBrowseRequest($options = [], $conditions = [], $responseID = null, $responseData = []) { // apply endpoint filter if (!empty($_REQUEST['endpoint'])) { if (!($Endpoint = Endpoint::getByHandle($_REQUEST['endpoint']))) { return static::throwNotFoundError('Endpoint not found'); } $conditions['EndpointID'] = $Endpoint->ID; $responseData['Endpoint'] = $Endpoint; } // apply method filter if (!empty($_REQUEST['method'])) { $conditions['Method'] = $_REQUEST['method']; } // apply path filter if (!empty($_REQUEST['path-substring'])) { $conditions[] = 'Path LIKE "%' . DB::escape($_REQUEST['path-substring']) . '%"'; } // apply path filter if (!empty($_REQUEST['query-substring'])) { $conditions[] = 'Query LIKE "%' . DB::escape($_REQUEST['query-substring']) . '%"'; } // apply IP filter if (!empty($_REQUEST['ip'])) { if (!filter_var($_REQUEST['ip'], FILTER_VALIDATE_IP)) { return static::throwError('IP is invalid'); } $conditions['ClientIP'] = ip2long($_REQUEST['ip']); } // apply key filter if (!empty($_REQUEST['key'])) { if (!($Key = Key::getByKey($_REQUEST['key']))) { return static::throwError('key is invalid'); } $conditions['KeyID'] = $Key->ID; } // apply time filter if (!empty($_REQUEST['time-max']) && ($timeMax = strtotime($_REQUEST['time-max']))) { $conditions[] = 'Created <= "' . date('Y-m-d H:i:s', $timeMax) . '"'; } if (!empty($_REQUEST['time-min']) && ($timeMin = strtotime($_REQUEST['time-min']))) { $conditions[] = 'Created >= "' . date('Y-m-d H:i:s', $timeMin) . '"'; } // apply type filter if (!empty($_REQUEST['type'])) { if ($_REQUEST['type'] == 'ping') { $conditions['Class'] = PingTransaction::class; } elseif ($_REQUEST['type'] == 'consumer') { $conditions['Class'] = Transaction::class; } } return parent::handleBrowseRequest($options, $conditions, $responseID, $responseData); }
public static function handleRequest() { $GLOBALS['Session']->requireAccountLevel('Staff'); if (empty($_REQUEST['endpoint'])) { return static::throwInvalidRequestError('endpoint required'); } elseif (!($Endpoint = Endpoint::getByHandle($_REQUEST['endpoint']))) { return static::throwNotFoundError('Endpoint not found'); } $cachedResponses = $Endpoint->getCachedResponses(); $limit = isset($_GET['limit']) && ctype_digit($_GET['limit']) ? (int) $_GET['limit'] : static::$defaultLimit; $offset = isset($_GET['offset']) && ctype_digit($_GET['offset']) ? (int) $_GET['offset'] : 0; return static::respond('cachedResponses', ['success' => true, 'data' => $limit ? array_slice($cachedResponses, $offset, $limit) : $cachedResponses, 'total' => count($cachedResponses), 'limit' => $limit, 'offset' => $offset, 'Endpoint' => $Endpoint]); }
public static function handleRequest() { // get request totals for trailing week if (false === ($weeklyRequestsByEndpoint = Cache::fetch('endpoints-requests-week'))) { $weeklyRequestsByEndpoint = array_map('intval', DB::valuesTable('EndpointID', 'requests', 'SELECT' . ' SUBSTRING_INDEX(@context := SUBSTRING_INDEX(`Key`, "/", 2), "/", -1) AS EndpointID,' . ' SUM(Value) AS requests' . ' FROM `%s`' . ' WHERE' . ' `Timestamp` BETWEEN DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 1 WEEK) AND CURRENT_TIMESTAMP AND ' . ' `Key` LIKE "endpoints/%%" AND' . ' `Key` REGEXP "^endpoints/[[:digit:]]+/requests$"' . ' GROUP BY EndpointID', [MetricSample::$tableName])); Cache::store('endpoints-requests-week', $weeklyRequestsByEndpoint, static::$popularityTTL); } // get public endpoints if (false === ($publicEndpointIds = Cache::fetch('endpoints-public'))) { try { $publicEndpointIds = array_map('intval', DB::allValues('ID', 'SELECT ID FROM `%s` WHERE Public', Endpoint::$tableName)); } catch (TableNotFoundException $e) { $publicEndpointIds = []; } Cache::store('endpoints-public', $publicEndpointIds, static::$publicTTL); } // fetch endpoint instances $publicEndpoints = array_map(function ($endpointId) { return Endpoint::getByID($endpointId); }, $publicEndpointIds); // determine requested order if (!empty($_GET['order']) && $_GET['order'] == 'alpha') { $order = 'alpha'; $sortFn = function ($a, $b) { return strcasecmp($a->Path, $b->Path); }; } elseif (!empty($_GET['order']) && $_GET['order'] == 'newest') { $order = 'newest'; $sortFn = function ($a, $b) { if ($a->ID == $b->ID) { return 0; } return $a->ID < $b->ID ? 1 : -1; }; } else { $order = 'popularity'; $sortFn = function ($a, $b) use($weeklyRequestsByEndpoint) { $a = isset($weeklyRequestsByEndpoint[$a->ID]) ? $weeklyRequestsByEndpoint[$a->ID] : 0; $b = isset($weeklyRequestsByEndpoint[$b->ID]) ? $weeklyRequestsByEndpoint[$b->ID] : 0; if ($a == $b) { return 0; } return $a < $b ? 1 : -1; }; } // apply order usort($publicEndpoints, $sortFn); static::respond('home', ['data' => $publicEndpoints, 'order' => $order]); }
public static function handleRequest() { if (empty(Site::$pathStack) || empty(Site::$pathStack[0])) { return static::throwInvalidRequestError(); } // get endpoint if (!($Endpoint = Endpoints\Endpoint::getFromPath(Site::$pathStack))) { return static::throwNotFoundError('Endpoint not found'); } // check access if (!$Endpoint->Public) { $GLOBALS['Session']->requireAccountLevel('Staff'); } return static::respond('docs', $Endpoint->getSwaggerData()); }
public static function handleRequest() { $GLOBALS['Session']->requireAccountLevel('Staff'); if (empty($_GET['time-max']) || !($timeMax = strtotime($_GET['time-max']))) { $timeMax = time(); } if (empty($_GET['time-min']) || !($timeMin = strtotime($_GET['time-min']))) { $timeMin = $timeMax - 3600 * 24 * 7; // 1 week } if (!empty($_GET['endpoint'])) { if (!($Endpoint = Endpoint::getByHandle($_GET['endpoint']))) { return static::throwNotFoundError('endpoint not found'); } } $topUsers = DB::allRecords('SELECT' . ' @user := SUBSTRING_INDEX(SUBSTRING_INDEX(`Key`, "/", -2), "/", 1) AS User,' . ' SUBSTRING_INDEX(@user, ":", 1) AS UserType,' . ' SUBSTRING_INDEX(@user, ":", -1) AS UserIdentifier,' . ' SUM(Value) AS TotalRequests,' . ' MIN(Timestamp) AS EarliestRequest,' . ' MAX(Timestamp) AS LatestRequest' . ' FROM `%s`' . ' WHERE' . ' `Timestamp` BETWEEN "%s" AND "%s" AND ' . ' `Key` LIKE "endpoints/%s/users/%%/requests"' . ' GROUP BY User' . ' ORDER BY TotalRequests DESC' . ' LIMIT %u', [MetricSample::$tableName, date('Y-m-d H:i:s', $timeMin), date('Y-m-d H:i:s', $timeMax), $Endpoint ? $Endpoint->ID : '%', !empty($_GET['limit']) && ctype_digit($_GET['limit']) ? $_GET['limit'] : 20]); return static::respond('topUsers', ['data' => $topUsers, 'Endpoint' => $Endpoint]); }
public static function handleEndpointsRequest(Key $Key) { if ($endpointId = static::shiftPath()) { $Endpoint = Endpoint::getByID($endpointId); if (!$Endpoint || !in_array($Endpoint, $Key->Endpoints)) { return static::throwNotFoundError('Requested endpoint not added to this key'); } return static::handleEndpointRequest($Key, $Endpoint); } switch ($_SERVER['REQUEST_METHOD']) { case 'GET': return static::respond('keyEndpoints', ['data' => KeyEndpoint::getAllByWhere(['KeyID' => $Key->ID])]); case 'POST': $GLOBALS['Session']->requireAccountLevel('Staff'); if (empty($_POST['EndpointID']) || !($Endpoint = Endpoint::getByID($_POST['EndpointID']))) { return static::throwInvalidRequestError('Valid EndpointID must be provided'); } if (KeyEndpoint::getByWhere(['KeyID' => $Key->ID, 'EndpointID' => $Endpoint->ID])) { return static::throwInvalidRequestError('Provided endpoint already added to this key'); } $KeyEndpoint = KeyEndpoint::create(['KeyID' => $Key->ID, 'EndpointID' => $Endpoint->ID], true); return static::respond('keyEndpointAdded', ['success' => true, 'data' => $KeyEndpoint]); break; default: return static::throwInvalidRequestError('Method not supported'); } }
<?php namespace Gatekeeper; use DB; use HttpProxy; use Gatekeeper\Alerts\TestFailed; use Gatekeeper\Endpoints\Endpoint; use Gatekeeper\Transactions\PingTransaction; // find all endpoints that are overdue or near due for a ping $endpoints = Endpoint::getAllByQuery('SELECT Endpoint.*' . ' FROM `%s` Endpoint' . ' WHERE' . ' Endpoint.PingFrequency IS NOT NULL AND' . ' IFNULL((' . ' SELECT Created FROM `%s` Transaction WHERE EndpointID = Endpoint.ID AND Class = "%s" ORDER BY ID DESC LIMIT 1' . ' ), 0) < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL (Endpoint.PingFrequency * 0.7) MINUTE)', [Endpoint::$tableName, PingTransaction::$tableName, DB::escape(PingTransaction::class)]); // loop through all endpoints to execute, test, and record the ping URI foreach ($endpoints as $Endpoint) { printf('Testing endpoint %s...', $Endpoint->getTitle()); // execute and capture request // TODO: use curl_multi_exec somehow? $response = HttpProxy::relayRequest(['autoAppend' => false, 'autoQuery' => false, 'url' => rtrim($Endpoint->InternalEndpoint, '/') . '/' . ltrim($Endpoint->PingURI, '/'), 'interface' => ApiRequestHandler::$sourceInterface, 'timeout' => 15, 'timeoutConnect' => 5, 'returnResponse' => true]); // evaluate success $testPassed = $response['info']['http_code'] == 200 && (!$Endpoint->PingTestPattern || preg_match($Endpoint->PingTestPattern, $response['body'])); // record transaction list($path, $query) = explode('?', $Endpoint->PingURI); $Transaction = PingTransaction::create(['Endpoint' => $Endpoint, 'ClientIP' => ip2long($response['info']['local_ip']), 'Method' => 'GET', 'Path' => $path, 'Query' => $query, 'ResponseTime' => $response['info']['starttransfer_time'] * 1000, 'ResponseCode' => $response['info']['http_code'], 'ResponseBytes' => $response['info']['size_download'], 'TestPassed' => $testPassed], true); // open alert if necessary, or close any existing one if (!$testPassed) { TestFailed::open($Endpoint, ['transactionId' => $Transaction->ID, 'request' => ['uri' => $Endpoint->PingURI], 'response' => ['status' => $Transaction->ResponseCode, 'headers' => $response['headers'], 'body' => $response['body'], 'bytes' => $Transaction->ResponseBytes, 'time' => $Transaction->ResponseTime]]); } else { $OpenAlert = TestFailed::getByWhere(['Class' => TestFailed::class, 'EndpointID' => $Endpoint->ID, 'Status' => 'open']); if ($OpenAlert) { $OpenAlert->Status = 'closed'; $OpenAlert->save(); }
<?php namespace Gatekeeper; use Gatekeeper\Endpoints\Endpoint; // detect endpoint if it has not already been set if (!$_EVENT['request']->getEndpoint()) { if (!($Endpoint = Endpoint::getFromPath($_EVENT['request']->getPathStack()))) { return ApiRequestHandler::throwNotFoundError('No endpoint was found that can handle this path'); } // trim path stack $_EVENT['request']->setPathStack(array_slice($_EVENT['request']->getPathStack(), substr_count($Endpoint->Path, '/') + 1)); // save determined endpoint to request object $_EVENT['request']->setEndpoint($Endpoint); }