Getting an error when trying to list the groups of my domain through the PHP Directory API. The really weird thing is that if I list the groups using OAuth playground, it works seamlessly with these scopes:
https://www.googleapis.com/auth/admin.directory.group
https://www.googleapis.com/auth/admin.directory.group.member
Note: I'm not using a service account do this. My email address has super admin access, so I'm assuming this should be fine when trying to manage groups and group members? I'm using Slim 3 to handle the applications details. My client is initialized and authenticated (according to a quick var_dump on the object), yet I get an error when trying to do:
$service = new \Google_Service_Directory($this->client);
var_dump($service->groups->listGroups(["domain" => "example.google.com"])); // placeholder domain for SO
and here is the error I'm receiving when trying to load the page so I can list the groups:
#0 /Applications/MAMP/htdocs/aslists/vendor/google/apiclient/src/Google/Http/REST.php(62): Google_Http_REST::decodeHttpResponse(Object(Google_Http_Request), Object(Google_Client))
#1 [internal function]: Google_Http_REST::doExecute(Object(Google_Client), Object(Google_Http_Request))
#2 /Applications/MAMP/htdocs/aslists/vendor/google/apiclient/src/Google/Task/Runner.php(174): call_user_func_array(Array, Array)
#3 /Applications/MAMP/htdocs/aslists/vendor/google/apiclient/src/Google/Http/REST.php(46): Google_Task_Runner->run()
#4 /Applications/MAMP/htdocs/aslists/vendor/google/apiclient/src/Google/Client.php(593): Google_Http_REST::execute(Object(Google_Client), Object(Google_Http_Request))
#5 /Applications/MAMP/htdocs/aslists/vendor/google/apiclient/src/Google/Service/Resource.php(240): Google_Client->execute(Object(Google_Http_Request))
#6 /Applications/MAMP/htdocs/aslists/vendor/google/apiclient/src/Google/Service/Directory.php(2122): Google_Service_Resource->call('list', Array, 'Google_Service_...')
#7 /Applications/MAMP/htdocs/aslists/src/controllers/DashboardController.php(23): Google_Service_Directory_Groups_Resource->listGroups(Array)
#8 [internal function]: a_s\controllers\DashboardController->index(Object(Slim\Http\Request), Object(Slim\Http\Response), Array)
...
#18 {main}
Why would the query fail using the PHP API, but succeed on OAuth playground?
Update 1
My apologies in advance, this will contain a lot of code...
I spent some time tinkering around following the PHP Quickstart example where my client_secret.json file is loaded into the application like so:
$this->client->setAuthConfigFile('/path/to/client_secret.json');
$credentialsPath = $this->expandHomeDirectory('/path/to/.credentials/admin-directory_v1-php-quickstart.json');
if(file_exists($credentialsPath)) {
$accessToken = file_get_contents($credentialsPath);
}
if(isset($accessToken)) {
$this->client->setAccessToken($accessToken);
}
if($this->client->isAccessTokenExpired()) {
$this->client->refreshToken($this->client->getRefreshToken());
file_put_contents($credentialsPath, $this->client->getAccessToken());
}
$this->service = new \Google_Service_Directory($this->client);
If I use this method, it works perfectly with my application – very strange. I did additional investigation between this method and using Google to sign into the application and I was unable to find anything conclusive. Given that, it should work if I use Google to sign in, but it still doesn't.
Here is how I am initializing the client:
class GoogleController extends Controller {
private static $APP_NAME = "Lists Manager";
private static $CLIENT_ID = "...";
private static $CLIENT_SECRET = "...";
private static $REDIRECT_URI = "postmessage";
private static $ACCESS_TYPE = "offline";
private static $scopes = [
\Google_Service_Oauth2::USERINFO_PROFILE,
\Google_Service_Oauth2::USERINFO_EMAIL,
\Google_Service_Directory::ADMIN_DIRECTORY_GROUP,
\Google_Service_Directory::ADMIN_DIRECTORY_GROUP_MEMBER,
\Google_Service_Directory::ADMIN_DIRECTORY_USER
];
protected $client;
public function __construct($container) {
parent::__construct($container);
$this->client = new \Google_Client();
$this->client->setApplicationName(self::$APP_NAME);
$this->client->setClientId(self::$CLIENT_ID);
$this->client->setClientSecret(self::$CLIENT_SECRET);
$this->client->setRedirectUri(self::$REDIRECT_URI);
$this->client->setScopes(self::$scopes);
$this->client->setAccessType(self::$ACCESS_TYPE);
}
}
From here, I have another controller which extends GoogleController that contains an ajax endpoint (i.e. where the access code it POSTed to). The code for this is shown below:
class LoginController extends GoogleController {
public function __construct($container) {
parent::__construct($container);
}
public function login($request, $response) {
if($this->client->isAccessTokenExpired()) {
unset($_SESSION[GoogleController::AS_PERSISTENT_CLIENT_OBJECT]);
$this->tokens = $this->client->authenticate($request->getBody()->getContents());
// $this->client->setAccessToken($this->token); // Not sure if I need to do this. Either way, I still get the exception
}
$_SESSION[GoogleController::AS_PERSISTENT_CLIENT_OBJECT] = serialize($this->client);
return $response->withStatus(200)
->withHeader('Content-Type', 'application/json')
->write($this->generateJsonSuccess('dashboard'));
}
}
Related
I am not able to understand the difference between getMethod() and getRealMethod() in Symphony5.
I am using:
Symfony 5
PHP 7.4.3
I have already read through this StackOverflow post, but I am still facing strange issues.
What I want to achieve: Currently if I give an empty body to a
POST/PUT request then symfony gives a 500 Internal Server Error. But,
I want to give a 400 Bad Request Error to the user along with a custom
error message like "Empty request body with PUT/POST not allowed"
I have created this Request Listener,
namespace App\EventListener;
use Exception;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
class RequestListener
{
/**
* #var LoggerInterface $logger
*/
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function __invoke(RequestEvent $event): ?Response
{
$request = $event->getRequest();
$method = $request->getMethod();
$realMethod = $request->getRealMethod();
// this echo below is just to know the values for $method and $realMethod for debugging
echo("method = $method and real method = $realMethod");
if (in_array($method, [Request::METHOD_POST, Request::METHOD_PUT]) && $request->get('data') === null) {
$this->logger->info("Method PUT/POST with an empty body");
return new Response(null, Response::HTTP_BAD_REQUEST);
}
}
}
I have also added this Request Listener to the services.yaml file as,
App\EventListener\RequestListener:
tags:
- { name: kernel.event_listener }
Now when I try to test this with postman I have the following response,
as you can see, the method I used is clearly shown as POST, symfony returns it as GET both for getMethod() and getRealMethod() (by the way, I am not sure why I get the echo message "method = GET and real method = GET" twice). And thus I am not able to check for the empty body for POST/PUT.
EDITED:
Running bin/console debug:event-dispatcher gives this output:
"kernel.request" event
----------------------
------- ---------------------------------------------------------------------------------------------- ----------
Order Callable Priority
------- ---------------------------------------------------------------------------------------------- ----------
#1 Symfony\Component\HttpKernel\EventListener\DebugHandlersListener::configure() 2048
#2 Symfony\Component\HttpKernel\EventListener\ValidateRequestListener::onKernelRequest() 256
#3 Nelmio\CorsBundle\EventListener\CorsListener::onKernelRequest() 250
#4 Symfony\Component\HttpKernel\EventListener\SessionListener::onKernelRequest() 128
#5 Symfony\Component\HttpKernel\EventListener\LocaleListener::setDefaultLocale() 100
#6 Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelRequest() 32
#7 ApiPlatform\Core\EventListener\QueryParameterValidateListener::onKernelRequest() 16
#8 Symfony\Component\HttpKernel\EventListener\LocaleListener::onKernelRequest() 16
#9 Symfony\Component\HttpKernel\EventListener\LocaleAwareListener::onKernelRequest() 15
#10 Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener::configureLogoutUrlGenerator() 8
#11 Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener::onKernelRequest() 8
#12 ApiPlatform\Core\EventListener\AddFormatListener::onKernelRequest() 7
#13 ApiPlatform\Core\EventListener\ReadListener::onKernelRequest() 4
#14 ApiPlatform\Core\Security\EventListener\DenyAccessListener::onSecurity() 3
#15 ApiPlatform\Core\EventListener\DeserializeListener::onKernelRequest() 2
#16 ApiPlatform\Core\Security\EventListener\DenyAccessListener::onSecurityPostDenormalize() 1
#17 App\EventSubscriber\EmptyRequestBodySubscriber::handleEmptyRequestBody() 1
#18 App\EventListener\RequestListener::__invoke() 0
#19 ApiPlatform\Core\Bridge\Symfony\Bundle\EventListener\SwaggerUiListener::onKernelRequest() 0
------- ---------------------------------------------------------------------------------------------- ----------
As you can see, my listener is found registered at #18,
#18 App\EventListener\RequestListener::__invoke() 0
I understand that I can add attributes to Monolog using the TagProcessor
$log->getMonolog()->pushProcessor(
new TagProcessor(
array(
'session: ' => session_id(),
'session2' => Session::getId()
)
)
);
And that I should extend Illuminate\Foundation\Bootstrap\ConfigureLogging.
This works ok, however when I try to add the session ID I get errors.
It seems that the session and Request objects are not available at the time the logger is produced?
[2016-03-30 18:52:21] local.ERROR: Symfony\Component\Debug\Exception\FatalThrowableError: Fatal error: Class 'Session' not found in /mywebapp/bootstrap/ConfigureLogging.php:43
Stack trace:
#0 /mywebapp/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/ConfigureLogging.php(60): Bootstrap\ConfigureLogging->configureTechOpsHandler(Object(Illuminate\Foundation\Application), Object(Illuminate\Log\Writer))
#1 /mywebapp/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/ConfigureLogging.php(30): Illuminate\Foundation\Bootstrap\ConfigureLogging->configureHandlers(Object(Illuminate\Foundation\Application), Object(Illuminate\Log\Writer))
#2 /mywebapp/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(203): Illuminate\Foundation\Bootstrap\ConfigureLogging->bootstrap(Object(Illuminate\Foundation\Application))
#3 /mywebapp/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(232): Illuminate\Foundation\Application->bootstrapWith(Array)
#4 /mywebapp/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(127): Illuminate\Foundation\Http\Kernel->bootstrap()
#5 /mywebapp/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(99): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter(Object(Illuminate\Http\Request))
#6 /mywebapp/public/index.php(53): Illuminate\Foundation\Http\Kernel->handle(Object(Illuminate\Http\Request))
#7 {main}
Gist for my custom ConfigureLogging.php file: https://gist.github.com/quixand/323227a08ede13e1536e51f37000674b
We used the following in the end, in ConfigureLogging.php
<?php namespace Bootstrap;
use Illuminate\Log\Writer;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Bootstrap\ConfigureLogging as BaseConfigureLogging;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;
use Monolog\Logger as Monolog;
class ConfigureLogging extends BaseConfigureLogging {
/**
* Configure the Monolog handlers for the application.
*
* #param \Illuminate\Contracts\Foundation\Application $app
* #param \Illuminate\Log\Writer $log
* #return void
*/
protected function configureDailyHandler(Application $app, Writer $log)
{
// Stream handlers
$logPath = $app->storagePath().'/logs/laravel.log';
$logStreamHandler = new RotatingFileHandler(
$logPath,
$app->make('config')->get('app.log_max_files', 5));
// Adds Username to each log line
$log->getMonolog()->pushProcessor( function ($record) {
$record['extra']['user'] = \Session::get("username");
$record['extra']['sessionId'] = \Session::getId();
return $record;
});
// Formatting
// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
$logFormat = "%datetime% [%level_name%] (%channel%) %extra%: %message% %context%\n";
$formatter = new LineFormatter($logFormat, null, true, true);
$logStreamHandler->setFormatter($formatter);
// push handlers
$logger = $log->getMonolog();
$logger->pushHandler($logStreamHandler);
}
}
You are missing use Session; at the top of your file
I have checked the official repo of monolog and put together below code, its working great and I am getting response in Slack. But the response are very verbose, How Can i get simple response, without Stack Trace.
And as suggested on doc I have added $monolog->pushProcessor(new WebProcessor($_SERVER)); but its not giving any information about requested URL and server.
class AppServiceProvider extends ServiceProvider {
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
if ($this->app->environment('production')) {
// Get The Logger
$monolog = Log::getMonolog();
// **********************************************************************************************
// I have tried official WebProcessor to get url, but its not giving me anything
// **********************************************************************************************
$monolog->pushProcessor(new WebProcessor($_SERVER));
$monolog->pushProcessor(function ($record) {
$record['extra']['session_id'] = Cookie::get(config('session.cookie'));
$record['extra']['request_id'] = Session::get('request_id');
return $record;
});
$slackHandler = new SlackHandler(env('SLACK_TOKEN'), '#sss-sslogs', 'sss-log', true, ':warning:', Logger::ERROR);
// **********************************************************************************************
// Setup Line Formatter but no luck
// **********************************************************************************************
// the default date format is "Y-m-d H:i:s"
$dateFormat = "Y n j, g:i a";
// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
$output = "%datetime% > %level_name% > %message% %context% %extra%\n";
// finally, create a formatter
$formatter = new LineFormatter($output, $dateFormat);
$slackHandler->setFormatter($formatter);
$monolog->pushHandler($slackHandler);
}
}
Above code is giving me below response in Slack channel
exception 'Illuminate\Database\Eloquent\ModelNotFoundException' with message 'No query results for model [App\User].' in /home/vagrant/Code/App/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:151
Stack trace:
#0 /home/vagrant/Code/App/app/Http/Controllers/PortfolioController.php(30): Illuminate\Database\Eloquent\Builder->firstOrFail()
#1 [internal function]: App\Http\Controllers\PorofileController->show('users')
#2 /home/vagrant/Code/App/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(246): call_user_func_array(Array, Array)
#3 /home/vagrant/Code/App/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(162): Illuminate\Routing\Controller->callAction('show', Array)
#4 /home/vagrant/Code/App/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(107): Illuminate\Routing\ControllerDispatcher->call(Object(App\Http\Controllers\PortfolioController), Object(Illuminate\Routing\Route), 'show')
#5 [internal function]: Illuminate\Routing\ControllerDispatcher->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#6 /home/vagrant/Code/App/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#7 [internal function]: Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#8 /home/vagrant/Code/App/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(101): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#9 /home/vagrant/Code/App/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(108): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#10 /home/vagrant/Code/App/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(67): Illuminate\Routing\ControllerDispatcher->callWithinStack(Object(App\Http\Controllers\PortfolioController), Object(I…
Level
----------------
ERROR
Where is the requested URL and session data??
How can I get only below part with requested URL, I am not interested in Stack trace:
exception 'Illuminate\Database\Eloquent\ModelNotFoundException' with message 'No query results for model [App\User].' in /home/vagrant/Code/App/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:151
I've taken the Doctrine 2 and ZF2 installation, with pagination, and tried to apply a search function to the method.
The album list is accessible via the usual route http://myapp/album
My current code appears to build a usable query using doctrine's createQueryBuilder()
When I give no parameters to title/artist then the result of $qb->getDql(); is SELECT a FROM Album\Entity\Album a and it returns no error and paginated results fine.
My method;
public function indexAction()
{
$view = new ViewModel();
$repository = $this->getEntityManager()->getRepository('Album\Entity\Album');
$qb = $repository->createQueryBuilder('album');
$qb->add('select', 'a')
->add('from', 'Album\Entity\Album a');
if ($this->params()->fromQuery('title')) {
$qb->add('where','title like :title');
$qb->setParameter(':title', '%' . $this->params()->fromQuery('title') . '%');
}
if ($this->params()->fromQuery('artist')) {
$qb->add('where','artist like :artist');
$qb->setParameter(':artist', '%' . $this->params()->fromQuery('artist') . '%');
}
$q = $qb->getDql();
$paginator = new Paginator(new DoctrineAdapter(new ORMPaginator($this->getEntityManager()->createQuery($q))));
$paginator->setDefaultItemCountPerPage(10);
$page = (int)$this->params()->fromQuery('page');
if ($page) $paginator->setCurrentPageNumber($page);
$view->setVariable('paginator', $paginator);
return $view;
}
But when I pass a query through the URL http://myapp/album?title=In%20My%20Dreams the result of $qb->getDql(); is SELECT a FROM Album\Entity\Album a WHERE title like :title which returns the error below.
Zend\Paginator\Exception\RuntimeException
File:
/Users/luke/Sites/hotelio/vendor/zendframework/zendframework/library/Zend/Paginator/Paginator.php:637
Message:
Error producing an iterator
Stack trace:
#0 /myapp/module/Album/view/album/album/index.phtml(20): Zend\Paginator\Paginator->getIterator()
#1 /myapp/vendor/zendframework/zendframework/library/Zend/View/Renderer/PhpRenderer.php(507): include('/myapp...')
#2 /myapp/vendor/zendframework/zendframework/library/Zend/View/View.php(205): Zend\View\Renderer\PhpRenderer->render(Object(Zend\View\Model\ViewModel))
#3 /myapp/vendor/zendframework/zendframework/library/Zend/View/View.php(233): Zend\View\View->render(Object(Zend\View\Model\ViewModel))
#4 /myapp/vendor/zendframework/zendframework/library/Zend/View/View.php(198): Zend\View\View->renderChildren(Object(Zend\View\Model\ViewModel))
#5 /myapp/vendor/zendframework/zendframework/library/Zend/Mvc/View/Http/DefaultRenderingStrategy.php(102): Zend\View\View->render(Object(Zend\View\Model\ViewModel))
#6 [internal function]: Zend\Mvc\View\Http\DefaultRenderingStrategy->render(Object(Zend\Mvc\MvcEvent))
#7 /myapp/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php(468): call_user_func(Array, Object(Zend\Mvc\MvcEvent))
#8 /myapp/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php(207): Zend\EventManager\EventManager->triggerListeners('render', Object(Zend\Mvc\MvcEvent), Array)
#9 /myapp/vendor/zendframework/zendframework/library/Zend/Mvc/Application.php(347): Zend\EventManager\EventManager->trigger('render', Object(Zend\Mvc\MvcEvent))
#10 /myapp/vendor/zendframework/zendframework/library/Zend/Mvc/Application.php(322): Zend\Mvc\Application->completeRequest(Object(Zend\Mvc\MvcEvent))
#11 /myapp/public/index.php(12): Zend\Mvc\Application->run()
#12 {main}
I understand that the error is where the view is accessing the variable, but as the paginator handles the modified query it appears to get as far of the view before erroring. I am not really sure what is going on here.
For the avoidance of doubt, the original method which also operates without error;
public function indexAction()
{
$view = new ViewModel();
$repository = $this->getEntityManager()->getRepository('Album\Entity\Album');
$paginator = new Paginator(new DoctrineAdapter(new ORMPaginator($this->getEntityManager()->createQuery($repository->createQueryBuilder('album')))));
$paginator->setDefaultItemCountPerPage(10);
$page = (int)$this->params()->fromQuery('page');
if ($page) $paginator->setCurrentPageNumber($page);
$view->setVariable('paginator', $paginator);
return $view;
}
I was getting the same error. In my case the problem was "date.timezone" not being set in php.ini. This was causing the following code to actually catch a \DateTime() exception.
Zend\Paginator\Paginator
public function getIterator()
{
try {
return $this->getCurrentItems();
} catch (\Exception $e) {
throw new Exception\RuntimeException('Error producing an iterator', null, $e);
}
}
Apart from bad DQL, "error producing an iterator" can also occur when data/DoctrineORMModule/Proxy does not have the correct write permissions set on it:
chmod 755 data/DoctrineORMModule/Proxy
I have in my bootstrap
public function initRoutes()
{
$router = new Zend_Controller_Router_Rewrite();
$route = new Zend_Controller_Router_Route_Static('register', array('module'=>'members','controller'=>'register','action'=>'index'));
$router->addRoute('register',$route);
}
and when I go to http://domain.com/register
I get this error:
The following error occurred:
exception 'Zend_Controller_Dispatcher_Exception' with message 'Invalid controller specified (register)' in /var/www/html/beta/library/Zend/Controller/Dispatcher/Standard.php:241 Stack trace: #0 /var/www/html/beta/library/Zend/Controller/Front.php(936): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http)) #1 /var/www/html/beta/application/bootstrap.php(24): Zend_Controller_Front->dispatch() #2 /var/www/html/beta/public/index.php(8): require_once('/var/www/html/b...') #3 {main}
in the modules/members/controllers directory there is a RegisterController.php with the class Members_RegisterController
I am not sure what I am doing wrong, I have referred to the manual on static routes and it seems that this should work
Looks like you are adding a route to a router, but the router is not the one that belongs to your front controller. The router you created evaporates as soon as your initRoutes() function terminates.
You must either use the default router of your front controller:
$router = $frontCtrl->getRouter();
$router->addRoute( ... );
Or else if you create a new router, set that router to be the router for the front controller:
$router = new Zend_Controller_Router_Rewrite();
// ...add routes...
$frontCtrl->setRouter($router);