I am making requests from Product 1 to Product 2. 3 requests will be sent, the first is authentication via POST where a session will be set on the receiving server, two will be the GET requests to the REST action of choice and three will be the close call to unset the session by POST.
Below is a quick mock up of what is desired:
I can set the session from request one but when the second GET request is sent I think the Session no longer exists. How do I persist that session until the third request to unset it is sent?
Sender:
public function sendRestRequest($action, $params= array(), $method = 'GET')
{
try {
// Authenticate request
$this->sendAuthenticateRequest();
$this->client->setUri('https://product1/controller/'.$action);
// Set post parameter to the authentication token
$this->setRequestParams($params, false, 'GET');
// Make request
$restRequest = $this->client->request($method);
// Get response
$restResponse = $restRequest->getBody();
$this->debugging = 0;
if ($this->debugging == 1) {
echo '<b>Rest Request:</b>';
echo '<pre>';
echo $restRequest;
echo '<pre>';
echo '<b>Rest Response:</b>';
echo '<pre>';
echo $restResponse;
echo '<pre>';
}
// Check the request was successful
if ($restRequest->getStatus() != 200) {
throw new Zend_Exception('The request returned a '.$restRequest->getStatus().' status code');
}
// Clear parameters so another request can be sent
$this->client->resetParameters();
// Close request
$this->closeRequest();
return $restResponse;
} catch (Zend_Exception $e) {
$this->setError(500, $e->getMessage());
$this->generateXml();
}
}
Receiver:
public function authenticationIn($passPhrase)
{
try {
if (!isset($passPhrase)) {
throw new Zend_Rest_Exception('No authentication key is detected.');
}
// Construct pass key from pass phrase and the shared secret
$generatedKey = $this->generateApiKey($passPhrase);
// Get KEY setting value from global_settings
$storedKey = $this->getQuidApiKey();
if ($generatedKey != $storedKey) {
// Bad PASS_PHRASE key send a failed authenticate response
header('HTTP/1.1 500 Application Error');
$authObject = new \stdClass();
$authObject->success = false;
echo json_encode($authObject);
exit;
} else {
// This session needs to persist until the 3rd request to unset it
$_SESSION['auth'] = true;
return true;
}
} catch (Zend_Service_Exception $e) {
$this->setError(500, $e->getMessage());
$this->generateXml();
}
}
From the client's perspective it's just the cookie(s) that need to be preserved between requests. Take a look at the cookie jar functionality: http://framework.zend.com/manual/1.12/en/zend.http.client.advanced.html#zend.http.client.cookies (particularly example #3).
Related
class REST {
// this section is from http://www.tutorialsface.com/2016/02/simple-php-mysql-rest-api-sample-example-tutorial/
public function response($data,$status){
$this->_code = ($status)?$status:200;
$this->set_headers();
echo $data;
exit;
}
}
class API extends REST {
private function create_booking(){
if($this->get_request_method() != "POST"){
echo $this->response('Method Not Acceptable',406);
}
/*pseudocode for processing data if method is POST*/
Find whether record exists.
If exist {
set $message = "record found"
} else {
If not exist, insert record
set $message = "record inserted"
}
echo $this->response($message,200);
}
}
I would like to know, if let's say I have an API end point using the above method, when user is not using POST, will system stop processing after echo the error 406, or it will still continue after existing IF statement.
I have users submitting a new booking to this end point several times due to server did not response a message. End up I have duplicate bookings in my database.
What could lead to such incident?
Yes, it will execute further, but you need to stop further execution. You can add a simple return in your method below the echo message to quit the method:
if($this->get_request_method() != "POST"){
echo $this->response('Method Not Acceptable',406);
return;
}
I'm using the php-graph-sdk v. 5.5 to get information of a user facebook that signs up in a web. The problem is that there's no way to know if a token is valid or not, using the methods like
FacebookAuthenticationAccessToken->getExpiresAt()
and so on. Those methods always return null values even if the token is valid. I'd like to check the token validity before call the ->get() method of the main FacebookFacebook class. Is there any way to do ti?
Solved! This is not the getExpiresAt() method that must be call. This is only a getter that gets the same information you posted in the constructor of
$accessToken = new \Facebook\Authentication\AccessToken($this->accessToken, $expirationDate);
You must set the var $expirationDate to a valid timestamp, and this is directly the value that getExpiresAt() returns, so doesn't has sense to use to check the Access Token validation.
To get the correct expiration date of an access token, you must perform a remote call using a Request as follows:
try {
$response = $this->fb->get('/debug_token?input_token=' . $this->accessToken);
}
catch(\Facebook\Exceptions\FacebookResponseException $e) {
// When Graph returns an error
$this->lastError = 'FaReEx: ' . $e->getMessage();
return false;
}
catch(\Facebook\Exceptions\FacebookSDKException $e) {
// When validation fails or other local issues
$this->lastError = 'FaSDKEx: ' . $e->getMessage();
return false;
}
$graphObject = $response->getGraphObject();
return $graphObject->getProperty('expires_at'); // This is a DateTime php object
I am building a small class to handle api requests and I am running into an issue with error handling (I am also a novice at OOP so please with me) I need to restrict or throw an error with any methods in my class that require the user parameter to be set and I also need to samething if token has not been retreived and I can't seem to wrap my head around it.
This is what I have so far...
$user array is set in a config file outside of class like so (empty by default):
$user = array(
'user_email' = '',
'user_pass' = ''
);
Class for handling API (simplified for question)
class eventAPI {
private $user
private $token
public function __construct($user) {
$this->user = $user;
// if possible assign token when instantiated
$this->retrieve_token($user);
}
private function retreive_token($user) {
// Check if user parameter has been set
if($this->validate_user_parameter()) {
// use credentials to make HTTP request for token
$token = 'somerandomtoken';
// assign token property retreived value
$this->token = $token;
} else {
echo 'User parameter has not been set.' // Not real message just for testing
return FALSE;
}
}
public function show_all_events() {
// Check if token has been retreived
if($this->validate_token_retreived()) {
// Use token to retreive events list via HTTP request
} else {
echo 'API not active. No valid token detected'; // for testing purposes
return FALSE
}
}
// reduntant code... Can't wrap my head around another way for checking for token.
public function show_single_event() {
// Check if token has been retreived
if($this->validate_token_retreived()) {
// Use token to retreive events list via HTTP request
} else {
echo 'API not active. No valid token detected'; // for testing purposes
return FALSE
}
}
// This is mostly where I am confused how to solve.
private function validate_user_parameter() {
foreach($this->user as $key => $value) {
// Remove whitespace from value
$value = trim($value);
if(empty($value)) {
echo 'User credentials have not been set'; // for testing purposes
return FALSE;
}
}
}
private function validate_token_retreived() {
$result = FALSE;
// Bool value not sure if this is the best way to do this
if(isset($this->$token)) {
$result = TRUE;
}
return $result;
}
}
First issue: I need to validate the user parameter which is in an array so I can use with a private method to retrieve the token. I chose to use a foreach loop to check each value but it seems a little archaic.
Second Issue: I have a redundant check in place for each public method to check if token is valid. Is there a better way to do this with OOP? I have many methods that require the token.
In short, how can I make sure that once the class is instantiated a public method that will be used by end user will not fire if any validation fails. The user info only needs to be valid once when instantiated and then the token needs to be valid for most remaining methods.
You don't need to pass $user parameter to retreive_token function. You got it in class scope. Just use $this->user in the function to access it. Also you didn't use it in that function, so why u passed it?
You didn't send true in any function.
There's nothing wrong with for-each but you want to check array_map too. Atleast you're applying a function to every item in array. It can be usefull. ps: seems for-each still faster then array_map
Also you would want to check empty function on which cases it returns false
You can use multiple returns in a function. You dont need to set a variable to do that.
Example
private function validate_token_retreived()
{
if(isset($this->token))
return true;
return false;
}
You couldn't use else in most cases.
Example
public function show_all_events()
{
if($this->validate_token_retreived()) {
// Use token to retreive events list via HTTP request
// and return here
}
echo 'API not active. No valid token detected'; // for testing purposes
return FALSE; // do you really return here? seems you are just generating an output
}
I am new to Angular and Data Api with Slim.
I create data Api with Slim used by Angular. My api code is below:
include 'dbConfig.php';
require 'Slim/Slim.php';
\Slim\Slim::registerAutoloader();
$slim_app = new \Slim\Slim();
$slim_app->get('/getUser','getUser');
$slim_app->post('/updateUser','updateUser');
$slim_app->post('/newUser','newUser');
$slim_app->run();
function getUser(){
global $mysqli;
$result = $mysqli->query("select * from users");
if(!$result){ echo "not connect";}
$rows = array();
while($row = $result->fetch_assoc()){
$rows[] = $row;
}
//echo "<pre>";
print json_encode($rows);
}
My Controller
myApp.controller('Ctrl',function ($scope, $http) {
$http.get('api/getUser').
success(function(data, status, headers, config){
$scope.obj = data;
});
});
When i go to localhost/api/getUser i can see all my json data so that mean anyone could see. how can i only allow my Angular app to use my api so it is secure?
Right,
This is a complicated subject but in it's simplest form you need a login route that returns an API key to your client like so....
NOTE this is very basic code that I use for testing purposes, it's missing proper hashing of passwords and checking for access token expiry plus a fair bit of other security. It's simply to illustrate an easy example.
app->get('/login/:username/:password', function ($username, $password) use ($app, $db) {
//check the login details against the db...
// TODO Include hashing FIX BEFORE PRODUCTION
$db->where("userName", $username);
$db->where("password", $password);
$result = $db->getOne('Users');
if ($db->count === 0) {
setResponseCode(401, array("Error" => "Incorrect login details."));
return;
}
// The way this key is generated needs updating, only for testing purposes.
$key = date("Y-m-d");
$key = $key . $username;
$key = md5($key);
// Need to add expiry time to access tokens....
$expire = date("Y-m-d H:i:s", strtotime('+15 minutes'));
$data = Array ('accessToken' => $key);
$db->where('id', $result['id']);
if (!$db->update('Users', $data)) {
// Internal error - this shouldn't happen!
setResponseCode(500, array("Error" => "Database error."));
return;
}
// Slim by default returns 200 so no need to call setResponseCode
echo json_encode(array("Authorisation" => $key));
});
function setResponseCode($code, $arrayJson) {
global $app;
/*
Function to save repetition of response headers - params are:
Int Code - HTTP Response Code
Array array_json - an array to be serialized into a JSON response ie array("Key" => "Item")
*/
$app->response->setStatus($code);
$app->response->SetBody(json_encode($arrayJson));
$app->response->headers->set('Content-Type', 'application/json');
}
function authorise($key) {
// Middleware for routes that require auth!
//global $key;
global $userId;
global $app;
global $db;
// need to add check for token expiry here!
$db->where('accessToken', $key);
$results = $db->getOne('Users', null, array('id'));
if ($db->count === 0) {
setResponseCode(401, array("Error" => "Authorisation Failure"));
$app->stop();
//return false;
}
$userId = $results['id'];
return true;
}
Your client needs to store the key and add it to the parameters to any request you can either use headers or just simple parameters. Again I'd google slim, API, access token, tutorial as the terms because this is a very basic answer to a complicated subject.
Now you just simply add authorise($key) to the top of any routes that need it, if it's false it'll return 401 and stop any further execution. I would recommend doing some more reading though as this is a very, very basic answer.
Can anyone see a problem with this line: $emailtoken = md5($user['User']['password'].CAKE_SESSION_STRING);
As it gives the following error: Use of undefined constant CAKE_SESSION_STRING - assumed 'CAKE_SESSION_STRING'
It still creates a token but gives that error and then when using the token it says that it is invalid :/
Here is the full function:
function admin_resetpassword ( $token = null )
{
// User submits their email address
if (!empty($this->data['User']['email']))
{
// user submitted initial form
$user = $this->User->findByEmail($this->data['User']['email']);
if (empty($user))
{
$this->Session->setFlash('Unknown email.');
return;
}
else
{
$emailtoken = md5($user['User']['password'].CAKE_SESSION_STRING);
// send email (temp flash to test code)
$this->Session->setFlash($emailtoken);
return;
}
}
// If the token is not empty on the url
if (!empty($token))
{
$user = $this->User->find(array("MD5(User.password + '".CAKE_SESSION_STRING."')"=>$token));
if (empty($user))
{
$this->Session->setFlash('Invalid token.');
return;
}
if (!empty($this->data['User']['password']))
{
$user['User']['password'] = $this->data['User']['password'];
$this->user->save($user);
$this->Session->setFlash('New password set.');
$this->redirect('/');
}
$this->set('token', $token);
$this->render('newpassword2');
}
}
the problem is CAKE_SESSION_STRING is not defined (as stated in the error).
If you want to get the salt or cipherSeed, use Configure::read('Security.salt'); or $this-Session->id; But you know this session id is lost after certain inactivity period, right? You won't be able to get that session id back later on (unless you save it somewhere).