I am using Monolog in a Symfony 2.8 project. I have configured a mail handler to send me notification about all errors.
Since not all errors are important (e.g. PageNotFound for /apple-app-site-association) I would like to add a filter. I did this by adding a custom Handler:
Config:
services:
monolog.custom_handler.service:
class: AppBundle\Log\CustomHandler
monolog:
main:
type: service
id: monolog.custom_handler.service
level: error
handler: mail_buffer
mail_buffer:
type: buffer
handler: mailer
mailer:
type: swift_mailer
from_email: "my address"
to_email: "my address"
subject: ERROR!
formatter: monolog.formatter.extended
Handler:
namespace AppBundle\Log;
use Monolog\Handler\AbstractHandler;
class CustomHandler extends AbstractHandler {
public function handle(array $record) {
// Check if handling level was reached
if ($record['level'] < $this->level)
return false;
if (!empty($record)) {
$url = (isset($record['extra']) && isset($record['extra']['url']) ? $record['extra']['url'] : '');
$message = (isset($record['message']) ? $record['message'] : '');
// Well Known Pages
if (strpos($url, '/.well-known') === 0) {
//echo "...match 1</br>";
return true;
}
if ($url == '/apple-app-site-association') {
//echo "...match 2</br>";
return true;
}
if (strpos($url, '/apple-touch-icon') === 0) {
//echo "...match 3</br>";
return true;
}
...
}
// Record was NOT handled --> DO NOT stop bubbeling
return false;
}
}
Using the echo statements I can confirm, that the handler is called, and the if clause correctly matches. So, if /apple-app-site-association is called for example, the handler returns true. I would expect, that this stops bubbeling, and that thus the nested mail_buffer handler is NOT called anymore. This is not the case.
No matter if the handler return true or false, I still receive mail for all error.
What am I doing wrong? How can I stop the processing of filtered messages?
Related
I still have this problem after asking the same question here: JSON returned from auth endpoint was invalid, yet status code was 200 with no response. I've looked at similar questions and followed the
suggestions: setting my broadcast driver to 'pusher', uncommenting 'App/BroadcastServiceProvider' class in my app.config file, setting debug mode to false in my .env file, etc. I have also looked at pusher docs but the issue remains unresolved for me.
I have updated my previous attempt by adding '/broadcasting/auth/' auth endpoint and headers but still the same error. But I can now see a 302 redirect to the auth route then a 302 redirect to the login route then to the dashboard with a 200 response on laravel telescope, which I wasn't seeing before now. So this suggests to me that adding the auth endpoint ought to resolve the issue but it doesn't.
I also tried setting up and using a '/pusher/auth/' auth end point route and controller but it gave me a 'Failed to load resource: the server responded with a status of 405 (Method Not Allowed)' along with "Error: Unable to retrieve auth string from auth endpoint - received status: 405 from /pusher/auth, but not the previous invalid json error. I get this with a 'get' request to the controller but a 500-internal server error with a 'post' request. I really don't know which is correct.
This is my bootstrap.js file:
import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');
// Enable pusher logging - don't include this in production
Pusher.logToConsole = true;
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
forceTLS: true,
authEndpoint: '/broadcasting/auth',
//authEndpoint: '/pusher/auth',
auth: {
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
}
}
});
This is one pusherController I created:
public function pusherAuth(Request $request)
{
$key = getenv('PUSHER_APP_KEY');
$secret = getenv('PUSHER_APP_SECRET');
$app_id = getenv('PUSHER_APP_ID');
$pusher = new Pusher($key, $secret, $app_id);
$auth = $pusher->socket_auth($_GET('channel_name'), $_GET('socket_id'));
return response($auth, 200);
}
I now know my vue frontend file that should receive and display the broadcast checks out and the issue has to do with resolving this pusher subscription error.
Any help will be appreciated.
Check your .env for the correct Broadcast driver:
BROADCAST_DRIVER=pusher
I was finally able to resolve this issue. The problem was entirely an authentication issue as the error messages pointed out. While I still don't know why the built in '/broadcast/auth' endpoint didn't work, my initial attempt to authenticate by creating a '/pusher/auth/' was wrong in the way I set up the route and controller.
The correct route set up should be 'post' and call a controller, using a closure based route didn't work for me. My previous (see above) implementation of the controller was also wrong.
This is the controller code that worked:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Pusher\Pusher;
class PusherController extends Controller
{
/**
* Authenticates logged-in user in the Pusher JS app
* For private channels
*/
public function pusherAuth(Request $request)
{
$user = auth()->user();
$socket_id = $request['socket_id'];
$channel_name =$request['channel_name'];
$key = getenv('PUSHER_APP_KEY');
$secret = getenv('PUSHER_APP_SECRET');
$app_id = getenv('PUSHER_APP_ID');
if ($user) {
$pusher = new Pusher($key, $secret, $app_id);
$auth = $pusher->socket_Auth($channel_name, $socket_id);
return response($auth, 200);
} else {
header('', true, 403);
echo "Forbidden";
return;
}
}
}
This is the final bootstrap.js file:
import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');
// Enable pusher logging - don't include this in production
Pusher.logToConsole = true;
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
forceTLS: true,
authEndpoint: '/pusher/auth',
auth: {
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
}
}
});
And my route code in web.php:
Route::post('/pusher/auth', [PusherController::class, 'pusherAuth'])
->middleware('auth');
Pusher console log:
Pusher : : ["Event recd",{"event":"pusher_internal:subscription_succeeded","channel":"private-user.3","data":{}}]
vendor.js:41325 Pusher : : ["No callbacks on private-user.3 for pusher:subscription_succeeded"]
My understanding is that I need to undertake the following steps:
Make the users' roles read-only
Use security rules on the data which access the roles to control access
Check for the role in the router
There are various examples on the official documentation how to deal with the security rules, but I couldn't figure out how to check for the role in the router. Let's assume I have an admin-only area, if someone who is not an admin tries to access that page I want that user to be redirected.
I'm currently following the official example using UI-Router, so this is my code:
app.config(["$stateProvider", function ($stateProvider) {
$stateProvider
.state("home", {
// the rest is the same for ui-router and ngRoute...
controller: "HomeCtrl",
templateUrl: "views/home.html",
resolve: {
// controller will not be loaded until $waitForSignIn resolves
// Auth refers to our $firebaseAuth wrapper in the factory below
"currentAuth": ["Auth", function(Auth) {
// $waitForSignIn returns a promise so the resolve waits for it to complete
return Auth.$waitForSignIn();
}]
}
})
.state("account", {
// the rest is the same for ui-router and ngRoute...
controller: "AccountCtrl",
templateUrl: "views/account.html",
resolve: {
// controller will not be loaded until $requireSignIn resolves
// Auth refers to our $firebaseAuth wrapper in the factory below
"currentAuth": ["Auth", function(Auth) {
// $requireSignIn returns a promise so the resolve waits for it to complete
// If the promise is rejected, it will throw a $stateChangeError (see above)
return Auth.$requireSignIn();
}]
}
});
}]);
I'm guessing I'll have to check in the resolve for a user role, but how would I access the data from the database there?
Update:
I tried André's solution, but "waitForAuth" (console.log("test1") never triggers. "waitForSignIn" does though, but then nothing happens - there is no error message.
.state('superadmin-login', {
url: '/superadmin',
templateUrl: 'views/superadmin-login.html',
'waitForAuth': ['Auth', function (Auth) {
console.log('test1');
// $requireAuth returns a promise so the resolve waits for it to complete
// If the promise is rejected, it will throw a $stateChangeError (see above)
return Auth.refAuth().$waitForSignIn();
}],
})
.state('superadmin', {
url: '/center-of-the-universe',
templateUrl: 'views/superadmin.html',
resolve: {
// YOUR RESOLVES GO HERE
// controller will not be loaded until $requireAuth resolves
// Auth refers to our $firebaseAuth wrapper in the example above
'currentAuth': ['Auth', function (Auth) {
console.log('test2');
// $requireAuth returns a promise so the resolve waits for it to complete
// If the promise is rejected, it will throw a $stateChangeError (see above)
return Auth.refAuth().$requireSignIn();
}],
//Here i check if a user has admin rights, note that i pass currentAuth and waitForAuth to this function to make sure those are resolves before this function
hasAdminAccess: function (currentAuth, waitForAuth, Rights) {
console.log('test');
return Rights.hasAdminAccess(currentAuth);
}
}
})
Here's how i did it.
First i made a factory to check if the user has the correct rights:
angular.module('rights.services', [])
.factory('Rights', function ($q) {
var ref = firebase.database().ref();
return {
hasAdminAccess: function (user) {
var deferred = $q.defer();
ref.child("Rights").child("Admin").child(user.uid).once('value').then(function (snapshot) {
if (snapshot.val()) {
deferred.resolve(true);
}
else{
deferred.reject("NO_ADMIN_ACCESS");
}
});
return deferred.promise;
}
};
});
And secondly i use this factory inside the resolve:
.state('logged', {
url: '',
abstract: true,
templateUrl: helper.basepath('app.html'),
resolve: {
// YOUR RESOLVES GO HERE
// controller will not be loaded until $requireAuth resolves
// Auth refers to our $firebaseAuth wrapper in the example above
"currentAuth": ["Auth", function (Auth) {
// $requireAuth returns a promise so the resolve waits for it to complete
// If the promise is rejected, it will throw a $stateChangeError (see above)
return Auth.refAuth().$requireSignIn();
}],
"waitForAuth": ["Auth", function (Auth) {
// $requireAuth returns a promise so the resolve waits for it to complete
// If the promise is rejected, it will throw a $stateChangeError (see above)
return Auth.refAuth().$waitForSignIn();
}],
//Here i check if a user has admin rights, note that i pass currentAuth and waitForAuth to this function to make sure those are resolves before this function
hasAdminAccess: function (currentAuth, waitForAuth, Rights) {
return Rights.hasLightAccess(currentAuth);
}
})
})
Keep in mind the way you save user roles in firebase can be different from how i do it in this example. This is (part of) how it looks in firebase:
{"moderators":
{
"0123eeca-ee0e-4ff1-9d13-43b8914999a9" : true,
"3ce9a153-eea8-498f-afad-ea2a92d79950" : true,
"571fa880-102d-4372-be8d-328ed9e7c9de" : true
}
},
{"Admins":
{
"d3d4effe-318a-43e1-a7b6-d7faf3f360eb" : true
}
}
And the security rules for these nodes:
"Admins": {
"$uid": {
//No write rule so admins can only be added inside the firebase console
".read": "auth != null && auth.uid ==$uid"
}
},
"Moderators" : {
//Admins are able to see who the moderators are and add/delete them
".read" : "(auth != null) && (root.child('Admins').hasChild(auth.uid))",
".write" : "(auth != null) && (root.child('Admins').hasChild(auth.uid))",
"$uid": {
".read": "auth != null && auth.uid ==$uid"
}
}
My app has AJAX requests here and there, and I want to protect them with CSRF tokens. However, rather than generating and passing a CSRF token to the Twig renderer for use in the JavaScript, I'd like a CSRF token to be readily available in every html page that Twig renders.
I've seen that Laravel seems to put it in a meta tag, so I can easily grab it with JavaScript. But how to do this in Symfony? How can I insert this token in every page?
Or is this not good practice?
Of course this is a good practice!!
You have the built-in function csrf_token('key') in symfony/twig for that.
Example:
<a href="{{ path('product_remove', {id: product.id, csrf: csrf_token('product') }) }}"
class="btn btn-info">Remove</a>
From a controller, you just have to check it:
/**
* #Route("/product/remove/{csrf}/{id}", name="product_remove")
*/
public function removeProductAction($csrf, $id)
{
if ($csrf !== $this->get('security.csrf.token_manager')->getToken('product')->getValue()) {
throw new InvalidCsrfTokenException('Invalid CSRF token');
}
// delete $id
// cleans up url
return $this->redirectToRoute('product_list');
}
The answer: insert it as a twig global variable and then use Javascript to bind it to all requests.
config.yml
twig:
## ...
globals:
csrfTokenManager: '#security.csrf.token_manager'
base.html.twig
<script>
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader('x-csrf-token', '{{ csrfTokenManager.getToken('ajaxAuth') }}');
});
</script>
Receiving controller
public function receiveAction (Request $request)
{
$tokenManager = $this->get('security.csrf.token_manager');
$tokenId = 'ajaxAuth';
$token = new CsrfToken($tokenId, $request->headers->get('x-csrf-token'));
if (!$tokenManager->isTokenValid($token)) {
throw new HttpException(403, 'Go away h4x0r');
}
}
I am looking at this block of code:
firebase.init({
onAuthStateChanged: function(data) { // optional but useful to immediately re-logon the user when he re-visits your app
console.log(data.loggedIn ? "Logged in to firebase" : "Logged out from firebase");
if (data.loggedIn) {
console.log("user's email address: " + (data.user.email ? data.user.email : "N/A"));
}
}
});
It's from the nativescript-firebase plugin authentication readme. I suspect it's the firebase instance, but can't be sure. I looked at the firebase.android.js file that contains the onAuthStateChanged listener, which leads me to believe that's what it is.
data is a Json, that means, to had any information, in this case had ifnrmation of user, , if you see this "onAuthStateChanged" that means a variable been created and that will be use, how parameter from a method
Information of user
data.user.email
get a boolean value
if (data.loggedIn) {
....
}
New Variable listener
var listener= {
onAuthStateChanged: function(data) {
......
}
};
listener will be used how parameter
// add the listener:
firebase.addAuthStateListener(listener);
// stop listening to auth state changes:
firebase.removeAuthStateListener(listener);
// check if already listening to auth state changes
firebase.hasAuthStateListener(listener);
I want to upload a file with jquery-file-upload (blueimp) in cross domain to a symfony2 application.
To do this, client side :
SLjQuery(function () {
'use strict';
SLjQuery('#media_answer_answer').fileupload({
url: "http://recrutonline.dev/app_dev.php/api/media/questionnaire-test-media/uploads",
dataType: 'text',
formData: SLjQuery('form#answer_process_form').serializeArray(),
forceIframeTransport: true,
redirect: 'http://mywebsite.dev/result.html?%s',
done: function (e, data) {
console.log('upload ok ', data)
},
progressall: function (e, data) {
console.log(data.loaded/data.total);
var progress = parseInt(data.loaded / data.total * 100, 10);
SLjQuery('#progress .progress-bar').css(
'width',
progress + '%'
);
}
}).prop('disabled', !SLjQuery.support.fileInput)
.parent().addClass(SLjQuery.support.fileInput ? undefined : 'disabled')
;
});
in my controller symfony :
public function postMediaUploadAction(Request $request)
{
$requestData = $request->request->all();
$redirectResponse = $requestData['redirect']; // http://mywebsite.dev/result.html?%s
//...
//here process on data & get file uploaded
//...
//example of data I would send back to the client : http://mywebsite.dev/result.html?{'file':[{'name':'filetoupload.jpg'}]}
$response = str_replace("%s", "{'file':[{'name':'filetoupload.jpg'}]}", $redirectresponse); // is it the good method ?
return ??
}
Now client side response received :
in console.log of 'done' parameter of ajax function:
data.result -> undefined
data.textStatus -> "success"
I tried to return many things but result is still undefined. So do you know what kind of data I have to return ?
Are you using the OneupUploaderBundle?
If so, don't post the file to your own controller but send it to the Oneup uploader endpoint. You can then return custom data by implementing an event listener.
For instance:
namespace Foo\BarBundle\EventListener;
use Oneup\UploaderBundle\Event\PostPersistEvent;
class UploadListener
{
public function onUpload(PostPersistEvent $event)
{
if ($file = $event->getFile()) {
$response = $event->getResponse();
$response['file']['name'] = $file->getKey();
}
}
}
Register this event listener in your services.yml (or xml file):
# Event listener to handle uploaded files
foo_bundle.upload_listener:
class: Foo\BarBundle\EventListener\UploadListener
tags:
- { name: kernel.event_listener, event: oneup_uploader.post_persist.default_uploader, method: onUpload }
When you do want to handle the upload yourself, which I don't recommend, return a Symfony\Component\HttpFoundation\Response or even better a Symfony\Component\HttpFoundation\JsonResponse object. See documentation.