I want to write username like extra field to production log.
How can I do it?
I have this config in config.yml:
services:
monolog.formatter.logprocessor:
class: Monolog\Formatter\LineFormatter
arguments:
- "[%%datetime%%] [%%username%%] %%channel%%.%%level_name%%: %%message%%\n"
monolog.processor.logprocessor:
class: Acme\CoreBundle\Monolog\LogProcessor
tags:
- { name: monolog.processor, method: processRecord }
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: warning
formatter: monolog.formatter.logprocessor
When I tried inject security.context into LogProcessor, I get error:
[Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException]
Circular reference detected for service "monolog.processor.logprocessor", path: "router ->
monolog.logger.router -> monolog.processor.logprocessor -> security.context -> security.a
uthorization_checker -> security.authentication.manager -> security.user.provider.concrete
.chain_provider -> security.user.provider.concrete.main -> doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection -> monolog.logger.doctrine".
Here is the configuration
# Service Configuration
services:
monolog.formatter.html:
class: Monolog\Formatter\HtmlFormatter
monolog.processor.web_processor:
class: Monolog\Processor\WebProcessor
tags:
- { name: monolog.processor, method: __invoke }
monolog.processor.user:
class: Company\ToolBoxBundle\Services\Monolog\ExtraProcessor
arguments: ["#security.token_storage"]
tags:
- { name: monolog.processor }
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
And the service code
namespace Company\ToolBoxBundle\Services\Monolog;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class ExtraProcessor
{
private $tokenStorage = null;
private $user = null;
private $postParams = null;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function __invoke(array $record)
{
if (null !== $this->user) {
$record['extra']['user'] = $this->user->getUserDetails();
}
if (null !== $this->postParams) {
$record['extra']['postParams'] = $this->postParams;
}
return $record;
}
public function onKernelRequest(GetResponseEvent $event)
{
$postParams = $event->getRequest()->request->all();
if(false === empty($postParams)){
$this->postParams = serialize($postParams);
}
if (null === $token = $this->tokenStorage->getToken()) {
return;
}
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
$this->user = $user;
}
}
if you are using a symfony version >= 2.4, you can have a look at this bundle
that should help you doing what you want :)
Related
im have problem in monolog doctrine logging (in symfony 5):
framework.yaml:
monolog:
channels: [doctrine_channel]
handlers:
main:
channels: ["!event", "!doctrine_channel"]
doctrine:
type: service
channels: [doctrine_channel]
id: app.logger.doctrine_handler
services.yaml:
app.logger.doctrine_handler:
class: App\Util\DoctrineHandler
arguments:
- "#doctrine.orm.entity_manager"
src/Utils/DoctrineHandler.php:
<?
namespace App\Util;
use App\Entity\Logs;
use Doctrine\ORM\EntityManagerInterface;
use Monolog\Handler\AbstractProcessingHandler;
class DoctrineHandler extends AbstractProcessingHandler
{
private $initialized;
private $entityManager;
private $channel = 'doctrine_channel';
public function __construct(EntityManagerInterface $entityManager)
{
parent::__construct();
$this->entityManager = $entityManager;
}
protected function write(array $record): void
{
if (!$this->initialized) {
$this->initialize();
}
if ($this->channel != $record['channel']) {
return;
}
$log = new Logs();
//$log->setMessage($record['message']);
//$log->setLevel($record['level_name']);
$log->setMessage($record['message']);
$log->setLevel($record['level']);
$log->setLevelName($record['level_name']);
$log->setExtra($record['extra']);
$log->setContext($record['context']);
$this->entityManager->persist($log);
$this->entityManager->flush();
}
private function initialize()
{
$this->initialized = true;
}
}
Now in the sample controller file:
in TestController.php
// in use inject LoggerInterface $logger
$this->logger = $logger;
$this->logger->info('test');
This is not logging to mysql database what is problem ?
Thank you in advance for all the hints...
You need to to use bindings. Try this guide https://nehalist.io/logging-events-to-database-in-symfony/
And this https://symfony.com/blog/new-in-symfony-4-2-autowiring-by-type-and-name
I get some trouble when I would load symfony app:
php.CRITICAL: Type error: Argument 1 passed to Doctrine\Common\EventManager::addEventSubscriber() must implement interface Doctrine\Common\EventSubscriber, instance of optro\Help\ORM\Listener\MessageElasticaListener
See my service configuration:
helpdesk.listner.optro:
class: Optro\Help\ORM\Listener\MessageElasticaListener
arguments:
- '#fos_elastica.object_persister.optro.technical_assistance'
- '#fos_elastica.indexable'
- { index: technical_assistance, type: post, identifier: id }
tags:
- { name: doctrine.event_listener, event: elastica.index.index_post_populate }
This is my class MessageElasticaListener:
use Doctrine\Common\EventArgs;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use FOS\ElasticaBundle\Doctrine\Listener as ElasticaListener;
use optro\Help\Entity\HelpdeskMessage;
class MessageElasticaListener extends ElasticaListener
{
/**
* {#inheritdoc}
*/
private function isObjectIndexable($object)
{
return true;
}
/**
* {#inheritdoc}
*/
public function postPersist(LifecycleEventArgs $eventArgs)
{
if (!$eventArgs instanceof LifecycleEventArgs) {
return;
}
$entity = $eventArgs->getObject();
if ($entity instanceof HelpdeskMessage && $this->isObjectIndexable($entity->getTechnicalAssistance())) {
$this->objectPersister->replaceOne($entity->getTechnicalAssistance());
}
}
}
What's wrong ? Bad services configuration ?
I use symfony 3.4 and FOS Elasticasearch 5.0.3
[Symfony\Component\Debug\Exception\ContextErrorException]
Catchable Fatal Error: Argument 2 passed to Symfony\Component\Security\Core\Authentication\Provider\SimpleAuthenticationProvider::__construct() must implement interface Symfony\Component\Security\Core\User\UserProviderInterface, instance of Delivve\WebBundle\Service\WebKeyUsersService given, called in /home/delivve-webservice/app/cache/de_/ap_DevDebugProjectContainer.php on line 4611 and defined
What happens is that I have an api that works, but now I need to make the web service log face or google account, but this error of the above, follow this tutorial to make
http://nyrodev.info/fr/posts/286/Connexions-OAuth-Multiple-avec-Symfony-2-3
And apena in OAuthMembersService.php file includes the useSymfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface; because symfony complained of is not having such imports.
I'm really doubt
I implemented the following classes:
<?php
namespace Delivve\WebBundle\Security;
use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUser as BaseOAuthUser;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
class WebKeyUserProvider extends BaseOAuthUser {
protected $data;
public function __construct(UserResponseInterface $response) {
parent::__construct($response->getUsername());
$this->data = array(
'provider'=>$response->getResourceOwner()->getName(),
'providerId'=>$response->getUsername()
);
$vars = array(
'nickname',
'realname',
'email',
'profilePicture',
'accessToken',
'refreshToken',
'tokenSecret',
'expiresIn',
);
foreach($vars as $v) {
$fct = 'get'.ucfirst($v);
$this->data[$v] = $response->$fct();
}
}
public function getData() {
return $this->data;
}
/**
* {#inheritDoc}
*/
public function getRoles() {
return array('ROLE_OAUTH_USER');
}
}
<?php
namespace Delivve\WebBundle\Service;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthAwareUserProviderInterface;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use Delivve\WebBundle\Security\WebKeyUserProvider;
class WebKeyUsersService implements UserProviderFactoryInterface, OAuthAwareUserProviderInterface {
public function loadUserByUsername($username) {
throw new Exception('loadByUsername not implemented');
}
public function supportsClass($class) {
return $class === "Delivve\\WebBundle\\Security\\WebKeyUserProvider";
}
public function refreshUser(\Symfony\Component\Security\Core\User\UserInterface $user) {
if (!$this->supportsClass(get_class($user))) {
throw new UnsupportedUserException(sprintf('Unsupported user class "%s"', get_class($user)));
}
return $user;
}
public function loadUserByOAuthUserResponse(UserResponseInterface $response) {
return new OAuthUser($response);
}
public function create(ContainerBuilder $container, $id, $config)
{
// TODO: Implement create() method.
}
public function getKey()
{
// TODO: Implement getKey() method.
}
public function addConfiguration(NodeDefinition $builder)
{
// TODO: Implement addConfiguration() method.
}
}
And these are my configurations:
routingSecurityOAuth.yml
hwi_oauth_login:
resource: "#HWIOAuthBundle/Resources/config/routing/login.xml"
prefix: /login
hwi_oauth_redirect:
resource: "#HWIOAuthBundle/Resources/config/routing/redirect.xml"
prefix: /connect
facebook_login:
pattern: /login/check-facebook
google_login:
pattern: /login/check-google
web_target:
pattern: /target
defaults: { _controller: DelivveWebBundle:Security:oauthTarget }
service
services:
web_key_user_provider:
class: Delivve\WebBundle\Service\WebKeyUsersService
security
security:
providers:
web_key_user_provider:
id: web_key_user_provider
firewalls:
web_key:
pattern: ^/web/*
anonymous: ~
provider: web_key_user_provider
oauth:
resource_owners:
facebook: "/web/login/check-facebook"
google: "/web/login/check-google"
# linkedin: "/web/login/check-linkedin"
login_path: /web/login
failure_path: /web/login
check_path: /web/login_check
default_target_path: /web/target
oauth_user_provider:
service: web_key_user_provider
default:
anonymous: ~
access_control:
- { path: ˆ/web/target, roles: ROLE_OAUTH_USER }
- { path: ˆ/web/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
routing
web_key_register:
pattern: /webRegister
defaults: { _controller: DelivveWebBundle:Security:webRegister }
web_key:
resource: "#DelivveWebBundle/Resources/config/routingSecurityOAuth.yml"
prefix: /web/
config
hwi_oauth:
firewall_name: web_key
resource_owners:
facebook:
type: facebook
client_id: %facebook_client_id%
client_secret: %facebook_client_secret%
scope: email
infos_url: "https://graph.facebook.com/me?fields=username,name,email,picture.type(large)"
paths:
email: email
profilepicture: picture.data.url
options:
display: popup
google:
type: google
client_id: %google_client_id%
client_secret: %google_client_secret%
scope: "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
paths:
email: email
profilepicture: picture
I'm trying to create a custom monolog processor to attach the current user to an error mailer.
When declaring a service like so:
monolog.processor.mail:
class: MyVendor\Monolog\Processor\MailProcessor
arguments:
- #mailer
- #security.context
tags:
- { name: monolog.processor, method: processRecord }
I get a circular reference:
[Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException]
Circular reference detected for service "monolog.processor.mail",
path: "router -> monolog.logger.router -> monolog.processor.mail
-> security.context -> security.authentication.manager
-> fos_user.user_provider.username_email-> fos_user.user_manager
-> doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection
-> doctrine.dbal.logger -> monolog.logger.doctrine".
What would be the best practice solution here?
A related forum thread:
http://forum.symfony-project.org/viewtopic.php?t=40306&p=131081#p131143
This thread shows that:
Setter injection doesn't solve the issue (i tried this as well)
Injecting the container causes an infinitive recursion (this i have not confirmed)
Also tried this script http://pastebin.com/AuvFgTY3 to get the user from the session.
if ($this->session !== null) {
if ($this->session->has($this->securityKey)) {
$token = unserialize($this->session->get($this->securityKey));
$this->currentUser = $token->getUser();
}
}
This gave the following error:
Warning: ini_set(): A session is active. You cannot change the session module's ini settings at this time in
C:\inetpub\symfony23\vendor\symfony\symfony\src\Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler.php
on line 56
I do understand that the security.context has not yet been build for services which request the logger very early on. For my class it's not a problem since i will set the user to undefined. So ideally the security.context would be setter injected AFTER the security.context service has been created. However i can not change the priority on the logger to be constructed very late because it's needed early on.
So maybe the question resolves to: how to recreate the service again after security.context has been initialized? Not sure if scope prototype would help here??
Create handler on kernel request and extract user:
// src/AppBundle/Monolog/UserProcessor.php
namespace AppBundle\Monolog;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class UserProcessor
{
private $tokenStorage;
private $user;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function __invoke(array $record)
{
if (null !== $this->user) {
$record['extra']['user'] = $this->user->getUsername();
}
return $record;
}
public function onKernelRequest(GetResponseEvent $event)
{
if (null === $token = $this->tokenStorage->getToken()) {
return;
}
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
$this->user = $user;
}
}
Register new processor:
# app/config/services.yml
services:
monolog.processor.user:
class: AppBundle\Monolog\UserProcessor
arguments: ["#security.token_storage"]
tags:
- { name: monolog.processor }
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Symfony Documentation has problem
I'm trying to override SonataUserBundle's ProfileFormType.
I have added some extra fields to that form and all the fields render on the page. So that works. But now I'm wondering why the user's data doesn't load, as the firstname, lastname, ... is already known but just not shown in the textboxes of the form.
The overridden ProfileController class's editProfileAction:
/**
* #return Response
*
* #throws AccessDeniedException
*/
public function editProfileAction()
{
$user = $this->container->get('security.context')->getToken()->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
$form = $this->container->get('sonata.user.profile.form');
$formHandler = $this->container->get('sonata.user.profile.form.handler');
$process = $formHandler->process($user);
if ($process) {
$this->setFlash('fos_user_success', 'profile.flash.updated');
return new RedirectResponse($this->generateUrl('sonata_user_profile_show'));
}
// This doesn't show the firstname
die($form->getData()->getFirstname());
return $this->render('SonataUserBundle:Profile:edit_profile.html.twig', array(
'form' => $form->createView(),
'theme' => $this->container->getParameter('fos_user.template.theme')
));
}
The overridden ProfileFormHandler class's process function:
public function process(UserInterface $user)
{
$this->form->setData($user);
// This DOES show the firstname
die($this->form->getData()->getFirstname());
if ('POST' == $this->request->getMethod()) {
$this->form->bindRequest($this->request);
if ($this->form->isValid()) {
$user->upload();
$this->onSuccess($user);
return true;
}
// Reloads the user to reset its username. This is needed when the
// username or password have been changed to avoid issues with the
// security layer.
$this->userManager->reloadUser($user);
}
return false;
}
Services.yml:
services:
application_sonata_user.registration.form.type:
class: Application\Sonata\UserBundle\Form\Type\RegistrationFormType
arguments: [%fos_user.model.user.class%]
tags:
- { name: form.type, alias: application_sonata_user_registration }
application_sonata_user.profile.form.type:
class: Application\Sonata\UserBundle\Form\Type\ProfileType
arguments: [%fos_user.model.user.class%]
tags:
- { name: form.type, alias: application_sonata_user_profile }
application_sonata_user.search.form.type:
class: Application\Sonata\UserBundle\Form\Type\SearchFormType
arguments: [%fos_user.model.user.class%]
tags:
- { name: form.type, alias: application_sonata_user_search }
application_sonata_user.form.handler.profile:
class: Application\Sonata\UserBundle\Form\Handler\ProfileFormHandler
arguments: ["#fos_user.profile.form", "#request", "#fos_user.user_manager", "#ewz_search.lucene"]
scope: request
public: false
In the services.yml file, I had to put:
arguments: ["#sonata.user.profile.form", "#request", "#fos_user.user_manager", "#ewz_search.lucene"]
instead of
arguments: ["#fos_user.profile.form", "#request", "#fos_user.user_manager", "#ewz_search.lucene"]