/** * Delegate the request to the correct method and * wrap the response (including exceptions) in a * JsonDataResponse * @param SS_HTTPRequest $request * @param DataModel $model * @return string|JsonDataResponse|SS_HTTPResponse|null * @throws JsonDataResponse * @throws SS_HTTPResponse_Exception */ public function handleRequest(SS_HTTPRequest $request, DataModel $model) { $body = null; $verb = strtolower($request->httpMethod()); $availableVerbs = $this->config()->get('allowed_verbs'); $id = $request->param('ID'); $resourceType = 'List'; if ($id && is_numeric($id)) { $resourceType = 'Data'; } $methodName = $verb . $resourceType; try { if (in_array(strtoupper($verb), $availableVerbs) && $this->hasMethod($methodName)) { $body = $this->{$methodName}($request); } } catch (SS_HTTPResponse_Exception $exception) { $response = $exception->getResponse(); throw new JsonDataResponse_Exception($response->getBody(), $response->getStatusCode(), $response->getStatusDescription()); } if ($body instanceof JsonDataResponse) { return $body; } if ($body instanceof SS_HTTPResponse) { return new JsonDataResponse($body->getBody(), $body->getStatusCode(), $body->getStatusDescription()); } if (is_string($body)) { return new JsonDataResponse($body); } throw new JsonDataResponse_Exception("The URI requested has not been implement", 404); }
/** * Maps the http method to action handlers. Forces the response to have * application/json content type. Unhandled methods will return a 404 response code. * @param SS_HTTPRequest $request * @return mixed * @throws SS_HTTPResponse_Exception */ public function index(SS_HTTPRequest $request) { $action = ucfirst(strtolower($request->httpMethod())); $actionHandler = sprintf('handle%sAction', $action); if ($this->hasMethod($actionHandler)) { $response = $this->{$actionHandler}($request); $response->addHeader('Content-Type', 'application/json'); return $response; } throw new SS_HTTPResponse_Exception(json_encode(array('code' => 404, 'description' => 'The action has not been implemented')), 404); }
/** * @param \SS_HTTPRequest $request * @return array */ public function process(\SS_HTTPRequest $request) { $httpMethod = $request->httpMethod(); if ($httpMethod == 'POST' && $request->param('ID') == 'complete') { //get the payment dps txnref from the database //1. Get session id for transaction from state //2. Get payment from storage //3. Use txn ref from storage to complete the payment $sessionId = $this->state->getByKey(self::IDENTIFIER . '.sessionid'); if ($sessionId) { // get the PXFusion payment associated with this sessionid $payment = \DataList::create('StoredPXFusionPayment')->filter("SessionId", $sessionId)->first(); if ($payment instanceof \StoredPXFusionPayment && $payment->DpsTxnRef) { // Set the transaction status to processing $this->transaction->setStatus('Processing'); // Keep track of the payment status $paymentSuccessful = false; $storedPxPostPayment = false; $paymentResponse = $this->paymentService->completeTransaction($payment->DpsTxnRef); if ($paymentResponse instanceof PXPostPaymentResponse) { // Store the payment response $storedPxPostPayment = $this->storage->process($paymentResponse)[Backend::IDENTIFIER]; $paymentResponse->updateTransaction($this->transaction); $paymentSuccessful = (bool) $paymentResponse->Success; } // store the transaction $storedTransaction = $this->storage->process($this->transaction)[Backend::IDENTIFIER]; $payment->ParentID = $storedTransaction->ID; if ($storedPxPostPayment && $storedTransaction) { // set the parents of each object $payment->PXPostPaymentID = $storedPxPostPayment->ID; $payment->write(); $storedPxPostPayment->ParentID = $storedTransaction->ID; $storedPxPostPayment->write(); } else { $payment->write(); return ['Success' => false]; } return ['Success' => $paymentSuccessful, 'Complete' => true, 'Data' => $paymentResponse]; } } } elseif ($httpMethod == 'GET' && $request->param('ID') == 'check') { $paymentResponse = $this->paymentService->checkTransaction($request->getVar('sessionid')); $this->state->setByKey(self::IDENTIFIER . '.sessionid', $request->getVar('sessionid')); $this->storage->process($paymentResponse); if ($paymentResponse->StatusCode === 0) { return ['Success' => true, 'Data' => $paymentResponse]; } else { return ['Success' => false, 'CheckFailure' => true]; } } return ['Success' => false]; }
/** * @param SS_HTTPRequest $request * @return SS_HTTPResponse */ public function update(\SS_HTTPRequest $request) { switch ($request->httpMethod()) { case 'POST': $this->checkSecurityToken(); return $this->createUpdate(); case 'GET': return $this->getUpdateStatus($this->getRequest()->param('ID')); default: return $this->getAPIResponse(['message' => 'Method not allowed, requires POST or GET/{id}'], 405); } }
/** * * @param SS_HTTPRequest $request * @return SS_HTTPResponse */ public function listProjects(SS_HTTPRequest $request) { $response = array('href' => Director::absoluteURL($this->Link()), 'projects' => array()); if ($request->httpMethod() != 'GET') { return $this->message('API not found', 404); } foreach (DNProject::get() as $item) { if ($item->canView($this->getMember())) { $response['projects'][] = array("name" => $item->Name, "href" => Director::absoluteURL($item->APILink(""))); } } return $this->getAPIResponse($response); }
/** * @param SS_HTTPRequest $request * @return SS_HTTPResponse */ public function fetch(SS_HTTPRequest $request) { if (!$this->record->canView($this->getMember())) { return $this->message('You are not authorized to do that on this environment', 403); } switch ($request->httpMethod()) { case 'GET': return $this->getFetch($this->getRequest()->param('ID')); case 'POST': return $this->createFetch(); default: return $this->message('API not found', 404); } }
public function handleRequest(SS_HTTPRequest $request, DataModel $model = null) { self::$is_at_root = true; $this->setDataModel($model); $this->pushCurrent(); $this->init(); if (!($site = Multisites::inst()->getCurrentSiteId())) { return $this->httpError(404); } $page = SiteTree::get()->filter(array('ParentID' => $site, 'URLSegment' => 'home')); if (!($page = $page->first())) { return $this->httpError(404); } $request = new SS_HTTPRequest($request->httpMethod(), $page->RelativeLink(), $request->getVars(), $request->postVars()); $request->match('$URLSegment//$Action', true); $front = new MultisitesFrontController(); $response = $front->handleRequest($request, $model); $this->popCurrent(); return $response; }
function handleRequest(SS_HTTPRequest $request, DataModel $model) { $this->request = $request; $method = $request->httpMethod(); if ($this->checkAccessAction($method)) { try { $request = $this->{$method}($request); // TODO: Abstract this out to API module, as it's application specific Session::save(); return $request; } catch (Exception $e) { if ($e instanceof SS_HTTPResponse_Exception) { throw $e; } elseif ($e instanceof RESTException) { $this->respondWithError(array('code' => $e->getCode(), 'exception' => $e)); } else { $this->respondWithError(array('code' => 500, 'exception' => $e)); } } } $this->respondWithError(array('code' => 403, 'exception' => new Exception('Method not allowed'))); }
/** * Main API hub switch * All requests pass through here and are redirected depending on HTTP verb and params * * @todo move authentication check to another methode * * @param SS_HTTPRequest $request HTTP request * @return string json object of the models found */ public function index(SS_HTTPRequest $request) { //check authentication if enabled if ($this->authenticator) { $policy = $this->config()->authentication_policy; $authALL = $policy === true; $authMethod = is_array($policy) && in_array($request->httpMethod(), $policy); if ($authALL || $authMethod) { $authResult = $this->authenticator->authenticate($request); if ($authResult instanceof RESTfulAPI_Error) { //Authentication failed return error to client return $this->error($authResult); } } } //pass control to query handler $data = $this->queryHandler->handleQuery($request); //catch + return errors if ($data instanceof RESTfulAPI_Error) { return $this->error($data); } //serialize response $json = $this->serializer->serialize($data); //catch + return errors if ($json instanceof RESTfulAPI_Error) { return $this->error($json); } //all is good reply normally return $this->answer($json); }
/** * Handle a form submission. GET and POST requests behave identically. * Populates the form with {@link loadDataFrom()}, calls {@link validate()}, * and only triggers the requested form action/method * if the form is valid. * * @param SS_HTTPRequest $request * @throws SS_HTTPResponse_Exception */ public function httpSubmission($request) { // Strict method check if ($this->strictFormMethodCheck) { // Throws an error if the method is bad... if ($this->formMethod != $request->httpMethod()) { $response = Controller::curr()->getResponse(); $response->addHeader('Allow', $this->formMethod); $this->httpError(405, _t("Form.BAD_METHOD", "This form requires a " . $this->formMethod . " submission")); } // ...and only uses the variables corresponding to that method type $vars = $this->formMethod == 'GET' ? $request->getVars() : $request->postVars(); } else { $vars = $request->requestVars(); } // Populate the form $this->loadDataFrom($vars, true); // Protection against CSRF attacks $token = $this->getSecurityToken(); if (!$token->checkRequest($request)) { $securityID = $token->getName(); if (empty($vars[$securityID])) { $this->httpError(400, _t("Form.CSRF_FAILED_MESSAGE", "There seems to have been a technical problem. Please click the back button, " . "refresh your browser, and try again.")); } else { // Clear invalid token on refresh $data = $this->getData(); unset($data[$securityID]); Session::set("FormInfo.{$this->FormName()}.data", $data); Session::set("FormInfo.{$this->FormName()}.errors", array()); $this->sessionMessage(_t("Form.CSRF_EXPIRED_MESSAGE", "Your session has expired. Please re-submit the form."), "warning"); return $this->controller->redirectBack(); } } // Determine the action button clicked $funcName = null; foreach ($vars as $paramName => $paramVal) { if (substr($paramName, 0, 7) == 'action_') { // Break off querystring arguments included in the action if (strpos($paramName, '?') !== false) { list($paramName, $paramVars) = explode('?', $paramName, 2); $newRequestParams = array(); parse_str($paramVars, $newRequestParams); $vars = array_merge((array) $vars, (array) $newRequestParams); } // Cleanup action_, _x and _y from image fields $funcName = preg_replace(array('/^action_/', '/_x$|_y$/'), '', $paramName); break; } } // If the action wasn't set, choose the default on the form. if (!isset($funcName) && ($defaultAction = $this->defaultAction())) { $funcName = $defaultAction->actionName(); } if (isset($funcName)) { Form::set_current_action($funcName); $this->setButtonClicked($funcName); } // Permission checks (first on controller, then falling back to form) if ($this->controller->hasMethod($funcName) && !$this->controller->checkAccessAction($funcName) && !$this->actions->dataFieldByName('action_' . $funcName)) { return $this->httpError(403, sprintf('Action "%s" not allowed on controller (Class: %s)', $funcName, get_class($this->controller))); } elseif ($this->hasMethod($funcName) && !$this->checkAccessAction($funcName)) { return $this->httpError(403, sprintf('Action "%s" not allowed on form (Name: "%s")', $funcName, $this->name)); } // TODO : Once we switch to a stricter policy regarding allowed_actions (meaning actions must be set // explicitly in allowed_actions in order to run) // Uncomment the following for checking security against running actions on form fields /* else { // Try to find a field that has the action, and allows it $fieldsHaveMethod = false; foreach ($this->Fields() as $field){ if ($field->hasMethod($funcName) && $field->checkAccessAction($funcName)) { $fieldsHaveMethod = true; } } if (!$fieldsHaveMethod) { return $this->httpError( 403, sprintf('Action "%s" not allowed on any fields of form (Name: "%s")', $funcName, $this->Name()) ); } }*/ // Validate the form if (!$this->validate()) { return $this->getValidationErrorResponse(); } // First, try a handler method on the controller (has been checked for allowed_actions above already) if ($this->controller->hasMethod($funcName)) { return $this->controller->{$funcName}($vars, $this, $request); // Otherwise, try a handler method on the form object. } elseif ($this->hasMethod($funcName)) { return $this->{$funcName}($vars, $this, $request); } elseif ($field = $this->checkFieldsForAction($this->Fields(), $funcName)) { return $field->{$funcName}($vars, $this, $request); } return $this->httpError(404); }
/** * Check if we're in a login request. If so, we're going to explicitly disable * restrictedobjects permission checks. This is poor, but dictated by the core * member login code performing writes prior to having a user context. * * @param \SS_HTTPRequest $request * @param \Session $session * @param \DataModel $model */ public function preRequest(\SS_HTTPRequest $request, \Session $session, \DataModel $model) { if (strtolower($request->httpMethod()) === 'post' && ($request->getURL() === 'Security/LoginForm' || $request->getURL() === 'Security/LostPasswordForm' || $request->getURL() === 'Security/ChangePasswordForm')) { Restrictable::set_enabled(false); } }
/** * Delete object of Class $model and ID $id * * @todo Respond with a 204 status message on success? * * @param string $model Model class * @param integer $id Model ID * @param SS_HTTPRequest $request Model ID * @return NULL|array NULL if successful or array with error detail */ function deleteModel($model, $id, SS_HTTPRequest $request) { if ($id) { $object = DataObject::get_by_id($model, $id); if ($object) { if (!RESTfulAPI::api_access_control($object, $request->httpMethod())) { return new RESTfulAPI_Error(403, "API access denied."); } $object->delete(); } else { return new RESTfulAPI_Error(404, "Record not found."); } } else { //shouldn't happen but just in case return new RESTfulAPI_Error(400, "Invalid or missing ID. Received '{$id}'."); } return NULL; }
/** * @param \SS_HTTPRequest $request * @return \SS_HTTPResponse */ public function abort(\SS_HTTPRequest $request) { if ($request->httpMethod() !== 'POST') { return $this->getAPIResponse(['message' => 'Method not allowed, requires POST'], 405); } $this->checkSecurityToken(); if (!self::can_abort_deployment($this->environment)) { return $this->getAPIResponse(['message' => 'You are not authorised to perform this action'], 403); } $deployment = \DNDeployment::get()->byId($request->postVar('id')); $errorResponse = $this->validateDeployment($deployment); if ($errorResponse instanceof \SS_HTTPResponse) { return $errorResponse; } try { $deployment->getMachine()->apply(\DNDeployment::TR_ABORT); } catch (\Exception $e) { return $this->getAPIResponse(['message' => $e->getMessage()], 400); } return $this->sendResponse(['message' => 'Deployment abort request successfully received', 'deployment' => $this->formatter->getDeploymentData($deployment)], 200); }
/** * Process input requests which are relevant to purchasables * * @param \SS_HTTPRequest $request * @return array Success/Failure */ public function process(\SS_HTTPRequest $request) { if (!in_array($request->httpMethod(), ['POST', 'PUT'])) { return $this->failed('Invalid method'); } if (!($id = $request->param('OtherID'))) { return $this->failed('No id provided'); } /** @var PurchasableInterface $purchasable */ $purchasable = $this->getPurchasable($request); if (!$purchasable instanceof $this->purchasableClass) { return $this->failed("Product doesn't exist"); } $action = $request->param('ID'); try { switch ($action) { case 'add': return $this->addPurchasable($request, $purchasable); break; case 'set': return $this->setPurchasable($request, $purchasable); break; case 'remove': return $this->removePurchasable($request, $purchasable); break; default: return $this->failed('Action not allowed'); break; } } catch (OverflowException $e) { return $this->failed('Cart bounds exceeded'); } }
/** * @param \SS_HTTPRequest $request * @return \SS_HTTPResponse */ public function reject(\SS_HTTPRequest $request) { if ($request->httpMethod() !== 'POST') { return $this->getAPIResponse(['message' => 'Method not allowed, requires POST'], 405); } $this->checkSecurityToken(); $deployment = \DNDeployment::get()->byId($request->postVar('id')); $errorResponse = $this->validateDeployment($deployment); if ($errorResponse instanceof \SS_HTTPResponse) { return $errorResponse; } // reject permissions are the same as can approve if (!self::can_approve($this->environment)) { return $this->getAPIResponse(['message' => 'You are not authorised to reject this deployment'], 403); } // if the current user is not the person who was selected for approval on submit, but they got // here because they still have permission, then change the approver to the current user if (\Member::currentUserID() !== $deployment->ApproverID) { $deployment->ApproverID = \Member::currentUserID(); } if ($request->postVar('rejected_reason')) { $deployment->RejectedReason = $request->postVar('rejected_reason'); } try { $deployment->getMachine()->apply(\DNDeployment::TR_REJECT); } catch (\Exception $e) { return $this->getAPIResponse(['message' => $e->getMessage()], 400); } return $this->getAPIResponse(['message' => 'Deployment request has been rejected', 'deployment' => $this->formatter->getDeploymentData($deployment)], 200); }
/** * @param SS_HTTPRequest $request * @return SS_HTTPResponse */ public function handleRequest(SS_HTTPRequest $request, DataModel $model = null) { self::$is_at_root = true; $this->setDataModel($model); $this->pushCurrent(); $this->init(); if (!DB::isActive() || !ClassInfo::hasTable('SiteTree')) { $this->response = new SS_HTTPResponse(); $this->response->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null)); return $this->response; } $request = new SS_HTTPRequest($request->httpMethod(), self::get_homepage_link() . '/', $request->getVars(), $request->postVars()); $request->match('$URLSegment//$Action', true); $controller = new ModelAsController(); $result = $controller->handleRequest($request, $model); $this->popCurrent(); return $result; }
/** * Returns the http method for this request. If the current environment is a development env, the method can be * changed with a `method` variable. * * @param SS_HTTPRequest $request the current request * @return string the used http method as string */ private function getMethodName($request) { $method = ''; if (Director::isDev() && ($varMethod = $request->getVar('method'))) { if (in_array(strtoupper($varMethod), array('GET', 'POST', 'PUT', 'DELETE', 'HEAD'))) { $method = $varMethod; } } else { $method = $request->httpMethod(); } return strtolower($method); }