I am using Module.php onBootstrapfunction to check using authentication. on Postman and with browser everything works fine but when unittesting with zend-test it is not able to access the getHeaders()
MY Module.php
public function onBootstrap(MvcEvent $e)
{
$app = $e->getTarget();
$locator = $app->getServiceManager();
$this->authorize($e);
}
public function authorize($e){
$sm = $e->getApplication()->getServiceManager();
$request = $sm->get('Request');
if($request->getHeaders('auth_token')){
return;
}
$response = $e->getResponse();
$model = new JsonModel(array(
"httpStatus" => 401,
"title" => "Token Not Found",
));
$e->getResponse()->setStatusCode(401);
$model->detail = "Auth Token not provided";
$model->setTerminal(true);
$e->setResult($model);
$e->setViewModel($model);
}
Now i tried to follow this but i couldn't because the code is so difficult to follow.
My test is something like that:
public function testIndexIsAccessibleAction() {
$headers = new \Zend\Http\Headers();
$headers->addHeaders([
"auth_token" => 'token'
]);
$this->getRequest()
->setMethod('GET')
->getHeaders()
->addHeaders($headers);
;
$this->dispatch('/action');
$this->assertResponseStatusCode(200);
}
Now i am checking every request in onBootstrap because nothing is accessible if user is not authentic. How do i do my unit testing correctly. and how should i change it to using Service or Factory?
Related
I'm using the Pole Emploi's API,but I encounter 401 error 25 minutes later, when my token expires.
I looked for a way to get a new token and retry the request, but no way for me to understand how Middlewares work, and if I should use a middleware for my needings.
On Guzzle's docs this is written :
Middleware functions return a function that accepts the next handler to invoke. This returned function then returns another function that acts as a composed handler-- it accepts a request and options, and returns a promise that is fulfilled with a response. Your composed middleware can modify the request, add custom request options, and modify the promise returned by the downstream handler.
And this is an example code from the docs :
use Psr\Http\Message\RequestInterface;
function my_middleware()
{
return function (callable $handler) {
return function (RequestInterface $request, array $options) use ($handler) {
return $handler($request, $options);
};
};
}
So I think I need to manage the "promise" to see if its HTTP code is 401, and then get a new token and retry the request ?
I'm lost, so I would appreciate if someone can explain me the logic of this with different words maybe :)
Thank you in advance.
It doesn't need to be that difficult, add a handler that takes care of the job, in combination with cache that expires.
If you don't use cache then I guess you could probably save it to a file along with a timestamp for expiration that you check against when fetching it.
class AuthenticationHandler
{
private $username;
private $password;
private $token_name = 'access_token';
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
public function __invoke(callable $handler)
{
return function (RequestInterface $request, array $options) use ($handler) {
if (is_null($token = Cache::get($this->token_name))) {
$response = $this->getJWT();
Cache::put($this->token_name, $token = $response->access_token, floor($response->expires_in));
}
return $handler(
$request->withAddedHeader('Authorization', 'Bearer '.$token)
->withAddedHeader('Api-Key', $this->api_key), $options
);
};
}
private function getJWT()
{
$response = (new Client)->request('POST', 'new/token/url', [
'form_params' => [
'grant_type' => 'client_credentials',
'username' => $this->username,
'password' => $this->password,
],
]);
return json_decode($response->getBody());
}
}
Then use it:
$stack = HandlerStack::create(new CurlHandler());
$stack->push(new AuthenticationHandler('username', 'password'));
$client = new GuzzleHttp\Client([
'base_uri' => 'https://api.com',
'handler' => $stack,
]);
Now you will always have a valid token, and you will never have to worry about it ever again.
I wouldn't recommend doing this as it can become hell to debug your application and as far as I am aware Guzzle doesn't really allow access to the client from middleware. Regardless you can use Promises to get around. If I were you I would refresh token before other requests, or refresh periodically. It might be fine if you are firing requests one by one, but in a Pool it will become a nightmare because you can end up having script fetch token too often and then some request ends up with out-dated token.
Anyway here is a rough example:
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
function my_middleware()
{
return function (callable $handler) {
return function (RequestInterface $request, array $options) use ($handler) {
/**
* #var $promise \GuzzleHttp\Promise\Promise
*/
$promise = $handler($request, $options);
return $promise->then(
function (ResponseInterface $response) use ($request, $options) {
if ($response->getStatusCode() === 404) {
var_dump($response->getStatusCode());
var_dump(strlen($response->getBody()));
// Pretend we are getting new token key here
$client = new Client();
$key = $client->get('https://www.iana.org/domains/reserved');
// Then we modify the failed request. For your case you use ->withHeader() to change the
// Authorization header with your token.
$uri = $request->getUri();
$uri = $uri->withHost('google.com')->withPath('/');
// New instance of Request
$request = $request->withUri($uri);
// Send the request again with our new header/URL/whatever
return $client->sendAsync($request, $options);
}
return $response;
}
);
};
};
}
$handlerStack = HandlerStack::create();
$handlerStack->push(my_middleware());
$client = new Client([
'base_uri' => 'https://example.org',
'http_errors' => false,
'handler' => $handlerStack
]);
$options = [];
$response = $client->request('GET', '/test', $options);
var_dump($response->getStatusCode());
var_dump(strlen($response->getBody()));
echo $response->getBody();
So I'm working with Google API Client for PHP and I have an OAuth flow that works,
class GoogleClient {
private static $client_id = "1050479587066-f64vq210hc2m15fdj4r77g8ml7jin30d.apps.googleusercontent.com";
private static $client_Secret = "CK8orQfPNpD9UgF0bqNJinVI";
private static $redirect_uri = '/return.php';
private static $access;
private static $client = null;
private static function checkForAccess(){
if(isset(self::$access)){
return true;
}
if(isset($_SESSION['GoogleAuth'])){
self::$access = $_SESSION['GoogleAuth'];
return true;
}
return false;
}
public static function GetClient(){
if(is_null(self::$client)){
$params = [
"client_id" => self::$client_id,
"client_secret" => self::$client_Secret,
"redirect_uri" => self::$redirect_uri,
"application_name" => "Test AdWords System"
];
if(self::checkForAccess() && self::isLoggedIn()){
$param["access_token"] = self::$access['access_token'];
}
//Create and Request to access Google API
$client = new Google_Client($params);
}
return $client;
}
public static function doLogin(){
$scopes = [ 'https://www.googleapis.com/auth/adwords', 'https://www.googleapis.com/auth/dfp', "https://www.googleapis.com/auth/userinfo.email"];
return self::GetClient()->createAuthUrl($scopes);
}
public static function doLoginFinal(){
if (!$code = $_GET['code']) {
throw new Exception("Auth Code is missing.");
}
$authResponse = self::GetClient()->authenticate($code);
if (isset($authResponse['error'])) {
throw new Exception(
"Unable to get access token.",
null,
new Exception(
"{$authResponse['error']} {$authResponse['error_description']}"
)
);
}
$_SESSION['GoogleAuth'] = $authResponse;
self::$access = $authResponse;
}
public static function isLoggedIn(){
if(self::checkForAccess()){
if(isset(self::$access)){
$expiresAt = #self::$access['created']+#self::$access['expires_in'];
return (time() < $expiresAt);
}
}
return false;
}
public static function GetExpiry(){
if(self::checkForAccess()){
return self::$access['created']+self::$access['expires_in'];
}
throw new Exception("The User is not logged into a google account.");
}
}
now this class is working I'm able to log in and I have the scope for google-adwords the problem comes about due to poor documentation for the googleads-php-lib
So from the example to getCampaigns it uses $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build(); but i don't have a file so i went into the OAuth2TokenBuilder file I'm unable to work out how i could give the already generated access tokens to the googleads objects.
I have double checked the google-php-api-client services repo and there is no adwords Service I can use.
I have been digging through the source files of the googleads-php-lib to see if I can find a method to implement this but so far I'm just getting stuck as everything seems to require specific parameter types so I can rig something to provide the details, but the code always seems to rely on multiple classes so I can't just build one that extends a class. and i pass that through.
Keys will be destoried after this test is working!
Well after days of digging around source files and hacking this and that I finally found an implementation that works.
After creating my manager account:
https://developers.google.com/adwords/api/docs/guides/signup
So this is the two new methods added to my GoogleClient Static Class
private static $developerToken = "";
private static function GetUserRefreshCredentials(){
return new UserRefreshCredentials(
null,
[
'client_id' => self::$client_id,
'client_secret' => self::$client_secret,
'refresh_token' => self::$access['refresh_token']
]
);
}
public function GetAdwordsSession(){
$builder = new AdWordsSessionBuilder();
$builder->defaultOptionals();
$builder->withDeveloperToken(slef::$developerToken);
return $builder->withOAuth2Credential(self::GetUserRefreshCredentials())->build();
}
I'm new to Twilio. I'm using Twilio for phone verification of my app. I'm using Laravel 5.5 for the backend & APIs. I've successfully sent the SMS to phone. I'm getting the call from Twilio but it says an application error. It doesn't read what I want to hear.
Below I'm giving every detail of my code.
Used composer require twilio/sdk for Twilio.
This is my Controller.
use Twilio\Rest\Client;
use Twilio\Twiml;
class AppUserController extends Controller{
private $account_sid;
private $auth_token;
private $twilio_number;
public function __construct(){
$this->account_sid = Config::get('envvalue.account_sid');
$this->auth_token = Config::get('envvalue.auth_token');
$this->twilio_number = Config::get('envvalue.twilio_number');
}
public function reVerification(Request $request){
$client = new Client($this->account_sid, $this->auth_token);
try {
$client->account->calls->create(
$receiverNumber,
$this->twilio_number,
array(
"url" => "http://demo.bitcanny.com/marine-admin/public/api/twiml/"
)
);
return response()->json([
'success' => true,
'statusCode' => '200',
'message' => 'Otp send again'
], 200);
}
catch (Exception $e) {
return $e->getMessage();
}
}
public function twiml(){
// A message for Twilio's TTS engine to repeat
$sayMessage = 'Hello.';
$twiml = new Twiml();
$twiml->say($sayMessage);
$response = Response::make($twiml, 200);
$response->header('Content-Type', 'text/xml');
return $response;
}
}
I found the solution. There's a silly mistake. I didn't use Response in the header of my controller.
use Response;
I am trying to configure guard with an OAuth 2 connection.
I am trying to do this with a redirection in the getCredentials function to the Microsoft login website but I can't make it work. I don't know how I can make it worked.
It seems there is no redirection possible in this function.
public function getCredentials(Request $request)
{
$provider = new Microsoft([
'clientId' => '0000000032624',
'clientSecret' => 'my-secret',
'redirectUri' => 'https://mysite/oauthlogin'
]);
if(!$request->query->has('code')){
// If we don't have an authorization code then get one
$authUrl = $provider->getAuthorizationUrl();
$request->getSession()->set('oauth2state', $provider->getState());
//This doesn't work
return new RedirectResponse($authUrl);
// Check given state against previously stored one to mitigate CSRF attack
}elseif ( empty($request->query->get('state')) || ($request->query->get('state')!==$request->getSession()->get('oauth2state')) ){
return null;
}else{
// Try to get an access token (using the authorization code grant)
$token = $provider->getAccessToken('authorization_code', [
'code' => $request->query->get('code')
]);
try {
//when log with microsoft, check if user is allowed
// We got an access token, let's now get the user's details
$user = $provider->getResourceOwner($token);
} catch (Exception $e) {
// Failed to get user details
}
}
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
return $userProvider->loadUserByUsername($user->getEmail());
}
public function checkCredentials($credentials, UserInterface $user)
{
// check credentials - e.g. make sure the password is valid
// no credential check is needed in this case
// return true to cause authentication success
return true;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$url = $this->router->generate('homepage');
return new RedirectResponse($url);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$data = array(
'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
// or to translate this message
// $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
);
$request->getSession()->set(Security::AUTHENTICATION_ERROR, $data);
$url = $this->router->generate('login');
return new RedirectResponse($url);
}
Function getCredentials() is not supposed to return a Response, it provide the credentials used in getUser().
In the getUser() documentation :
The credentials are the return value from getCredentials()
You may throw an AuthenticationException if you wish. If you return
null, then a UsernameNotFoundException is thrown for you.
In case of exception thrown, onAuthenticationFailure() is called and here you can return your RedirectResponse.
For more detailled informations, see the source code of the \Symfony\Component\Security\Guard\GuardAuthenticatorInterface which contains a lots of explanations in its methods.
Is there any way to mock response and request in Guzzle?
I have a class which sends some request and I want to test.
In Guzzle doc I found a way how can I mock response and request separately. But how can I combine them?
Because, If use history stack, guzzle trying to send a real request.
And visa verse, when I mock response handler can't test request.
class MyClass {
public function __construct($guzzleClient) {
$this->client = $guzzleClient;
}
public function registerUser($name, $lang)
{
$body = ['name' => $name, 'lang' = $lang, 'state' => 'online'];
$response = $this->sendRequest('PUT', '/users', ['body' => $body];
return $response->getStatusCode() == 201;
}
protected function sendRequest($method, $resource, array $options = [])
{
try {
$response = $this->client->request($method, $resource, $options);
} catch (BadResponseException $e) {
$response = $e->getResponse();
}
$this->response = $response;
return $response;
}
}
Test:
class MyClassTest {
//....
public function testRegisterUser()
{
$guzzleMock = new \GuzzleHttp\Handler\MockHandler([
new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);
$guzzleClient = new \GuzzleHttp\Client(['handler' => $guzzleMock]);
$myClass = new MyClass($guzzleClient);
/**
* But how can I check that request contains all fields that I put in the body? Or if I add some extra header?
*/
$this->assertTrue($myClass->registerUser('John Doe', 'en'));
}
//...
}
#Alex Blex was very close.
Solution:
$container = [];
$history = \GuzzleHttp\Middleware::history($container);
$guzzleMock = new \GuzzleHttp\Handler\MockHandler([
new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);
$stack = \GuzzleHttp\HandlerStack::create($guzzleMock);
$stack->push($history);
$guzzleClient = new \GuzzleHttp\Client(['handler' => $stack]);
First of all, you don't mock requests. The requests are the real ones you are going to use in production. The mock handler is actually a stack, so you can push multiple handlers there:
$container = [];
$history = \GuzzleHttp\Middleware::history($container);
$stack = \GuzzleHttp\Handler\MockHandler::createWithMiddleware([
new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);
$stack->push($history);
$guzzleClient = new \GuzzleHttp\Client(['handler' => $stack]);
After you run your tests, $container will have all transactions for you to assert. In your particular test - a single transaction. You are interested in $container[0]['request'], since $container[0]['response'] will contain your canned response, so there is nothing to assert really.