Asserting email was sent with mailhog dsn - symfony

Testing my email sending logic, doesn't catch the sent email to mailhog.
// Both ways don't work
$this->assertEmailCount(1, 'smtp://mailhog:1025');
$this->assertEmailCount(1);
Errors I got for both:
Failed asserting that the Transport smtp://mailhog:1025 has sent "1" emails (0 sent).
Failed asserting that the Transport has sent "1" emails (0 sent).
Email is sent. I see it in mailhog. Everytime I run the tests.
private function getMailer(array $mailConfig): MailerInterface
{
$dsn = match (strtolower($mailConfig['transport'])) {
'sendgrid' => 'smtp://mailhog:1025',
'mailchimp' => 'smtp://mailhog:1025',
default => 'smtp://mailhog:1025'
};
$transport = Transport::fromDsn($dsn);
return new Mailer($transport);
}
Posting the sending functionality as well but think it doesn't matter when email is sent. Is there something specific when testing with mailhog?

I got the same problem, and I just add
//...
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Mailer\Mailer;
public function __construct(
//...
private readonly EventDispatcherInterface $eventDispatcher
) {
}
public function getMailer(): Mailer
{
//...
$transport = Transport::fromDsn($dsn, $this->eventDispatcher);
return new Mailer($transport);
}
You can also disable delivery in your tests https://symfony.com/doc/current/mailer.html#disabling-delivery

Related

ASP.NET Core identity is not sending e-mail

In my Startup.cs I have
services.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, EmailSender>(i =>
new EmailSender(
Configuration["EmailSender:Host"],
Configuration.GetValue<int>("EmailSender:Port"),
Configuration.GetValue<bool>("EmailSender:EnableSSL"),
Configuration["EmailSender:UserName"],
Configuration["EmailSender:Password"]
));
and I have a class
public class EmailSender : IEmailSender
{
/* snip */
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
var client = new SmtpClient(_Host, _Port)
{
Credentials = new NetworkCredential(_UserName, _Password), EnableSsl = _EnableSSL
};
return client.SendMailAsync(
new MailMessage(_UserName, email, subject, htmlMessage) { IsBodyHtml = true }
);
}
}
I'm running smtp4dev.
When I try to register a user or forget a password no e-mail is sent. In a breakpoint in SendEmailAsync is not hit.
I don't understand what I need to do to get this thing to send an e-mail.
Do you? And if so then can you tell me what it is?
Is this documented somewhere?
If you're certain emails are not being sent for both email verifications and password resets, then please ignore this suggestion. However, if your emails get sent for email verifications but don't get sent for password resets, then please read on.
Does a break point in the constructor of your IEmailSender get hit? If so then make sure that the user you're trying to reset the password for has a verified email address. If an email address is not verified Identity will not send any of the other email notifications.
Alternatively, temporarily disable confirmed accounts and see if your emails are being sent.
services.AddDefaultIdentity<ApplicationUser>(options =>
{
options.SignIn.RequireConfirmedAccount = false;
})
Please do add the code snippet where and how you are using EmailSender class , since you have asked for the documentation , which you can refer to this https://learn.microsoft.com/en-us/aspnet/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity

How to verify Admob Rewarded Video Ad on a server?

Is there a way to verify if the Rewarded Video Ad was watched by client (OnAdRewarded) on a server? Is there any integration I can use with Google Cloud Functions?
I thought it's possible to verify a token sent by client to the server with admob admin SDK but it seems that it's not possible and we can only verify the ad on the client.
It is now possible with Server-Side Verification (SSV) Callbacks.
Server-side verification callbacks are URL requests, with query parameters expanded by Google, that are sent by Google to an external system to notify it that a user should be rewarded for interacting with a rewarded video ad. Rewarded video SSV (server-side verification) callbacks provide an extra layer of protection against spoofing of client-side callbacks to reward users.
Not sure this is relevant to Firebase but here are some details in case anyone is using Node / JS. You can use Node's inbuilt crypto library. First fetch the available Google AdMob verifier keys from https://gstatic.com/admob/reward/verifier-keys.json.
You'll then need to loop through the returned JSON keys array and grab the pem public key file string corresponding to the req.query.key_id parameter of your incoming req.url string.
Then the "message" we wish to verify signatures with is the incoming req.url substring inbetween the parameter ? symbol and &signature... strings.
Now we can verify easily:
const verifier = crypto.createVerify("sha256");
verifier.update(message);
if(verifier.verify(pem, req.query.signature, "base64"))
console.log("Ad was successfully verified.");
else
console.log("Ad could not be verified - quick call the cops !");
One caveat to beware of is you may need to unescape(...) your req.url string before you use it since some characters may have been escaped. I was stuck on that for a good hour or two. You can do this using e.g. Node's built in querystring library.
Not at the moment. The feature has been in closed beta for a while from what I've gathered recently. The last mention I could find was in the linked discussion where someone, from Google presumably, says the feature will be rolled out to the public soon. The post is from Jan 22.
https://groups.google.com/forum/#!topic/google-admob-ads-sdk/weXTAGZfYQ8
I know its a bit late but here is a piece of code that helped me. It is in javascript for Node users.
https://github.com/hypeben/admob-rewarded-ads-ssv
const queryString = require('query-string');
const crypto = require('crypto');
const axios = require('axios');
const GOOGLE_AD_KEY_URL = 'https://gstatic.com/admob/reward/verifier-keys.json';
/**
* Fetches the google public keys for the admob providers.
* These keys changes time to time.
*/
const getGoogleKeysMap = async () => {
let googleKeyRes = await axios.get(GOOGLE_AD_KEY_URL);
let {keys} = googleKeyRes.data;
if (!keys) {
throw new Error('No keys found from google keys');
}
/** For each of the keys array save it base 64 in decoded form in the key map */
let keyMap = {};
keys.forEach(k => {
keyMap[`${k.keyId}`] = crypto.createPublicKey(k.pem);
console.log(keyMap[`${k.keyId}`]);
});
return keyMap;
};
/**
* Verifies the callback url query params string,
* Resolves the promise if verification was successful, else fails.
* Wanna 'debug' then pass the second parameter as true.
* #param {String} queryUrl
* #param {Boolean} debug
*/
async function verify(queryUrl, debug) {
try {
if (typeof queryUrl !== "string") throw new TypeError("URL needs to be string!");
/**
* Request coming as callback from admob must contain the 'signature' and the 'user_id'.
* For more info https://developers.google.com/admob/android/rewarded-video-ssv
*/
const {signature, key_id} = queryString.parse(queryUrl);
if (!signature) {
throw new Error('No signature value exist in the URL param');
}
if(debug) {
console.debug('Signature and KeyId ---');
console.debug(signature, key_id);
// console.log('Signature and KeyId ---');
// console.log(signature, key_id);
}
let queryParamsString = queryUrl;
if (queryParamsString.indexOf('?') > -1) {
queryParamsString = queryUrl.split('?')[1];
}
if(debug) {
console.debug('Query param string ---');
// console.log('Query param string ---');
console.debug(queryParamsString);
// console.log(queryParamsString);
}
/**
* As per admob,
* The last two query parameters of rewarded video SSV callbacks are always signature and key_id, in that order.
* The remaining query parameters specify the content to be verified.
*/
let contentToVerify = queryParamsString.substring(0, queryParamsString.indexOf('signature') -1);
if(debug) {
console.debug('Content to verify ---');
// console.log(contentToVerify);
// console.log('Content to verify ---');
console.debug(contentToVerify);
}
let keyMap = await getGoogleKeysMap();
if(keyMap[`${key_id}`]) {
let publicKey = keyMap[`${key_id}`];
const verifier = crypto.createVerify('RSA-SHA256');
verifier.update(contentToVerify);
let result = verifier.verify(publicKey, signature, 'base64');
if (result) {
console.debug('Result ---');
console.debug(result);
return true;
} else {
console.debug('Failure ---');
console.debug(result);
throw new Error('Invalid Signature Supplied');
}
} else {
console.debug('Key id provided doesn\'t exist ---');
throw new Error('Key id provided doesn\'t exist in the google public keys');
}
} catch (error) {
}
}
module.exports.verify = verify;
If you guys looking for a simple way on Golang for the Admob SSV.
Just use this hiyali/go-lib-ssv, hope to save your life :)

Laravel Unit Testing - add cookie to request?

I want to send a cookie with json POST:
public function testAccessCookie()
{
$response = $this->json('POST', route('publications'))->withCookie(Cookie::create('test'));
//some asserts
}
publications route has some middleware:
public function handle($request, Closure $next)
{
Log::debug('cookie', [$request->cookies]);
//cookie validation
return $next($request);
}
But while running testAccessCookie(), there is [null] inside log. No cookies attached.
What's wrong?
There is no such problem with real (in-browser) requests.
You can add cookies to calls in tests:
$cookies = ['test' => 'value'];
$response = $this->call('POST', route('publications'), [], $cookies);
See https://laravel.com/api/5.4/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.html#method_call
However you will run into a cookie encryption problem. You can temporarily disable cookies during testing with:
use Illuminate\Cookie\Middleware\EncryptCookies;
/**
* #param array|string $cookies
* #return $this
*/
protected function disableCookiesEncryption($name)
{
$this->app->resolving(EncryptCookies::class,
function ($object) use ($name)
{
$object->disableFor($name);
});
return $this;
}
Adding $this->disableCookiesEncryption('test'); at the start of the test.
You may need to add headers to specify a json response.
This should work in recent versions (Laravel 6):
Either:
$this->disableCookieEncryption();
or:
$cookies = ['test' => encrypt('value', false)];
$response = $this->call('POST', route('publications'), [], $cookies);
Since Laravel 5.2 you get the \App\Http\Middleware\EncryptCookies::class middleware defined by default in the web middleware group and it will set all unencrypted cookies to null.
Unfortunately all cookies you send with $request->call(), $request->get() and $request->post() in unit testing are usually unencrypted and nothing in the official documentation tells you they need to be encrypted.
If you don't want to call $request->disableCookieEncryption() everytime, as a permanent solution you can simply redefine the isDisabled() method in App\Http\Middleware\EncryptCookies.php to ignore cookies encryption during unit testing.
Here is the implementation I made for Laravel 6.x, it should work on earlier versions too.
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* #var array
*/
protected $except = [
//
];
public function isDisabled($name)
{
if (app()->runningUnitTests()) {
return true; // Disable cookies encryption/decryption during unit testing
}
return parent::isDisabled($name);
}
}

Symfony/Silex dispatch events async

I've setup Silex to receive webhooks from, for example, Github. In this case Github sends all the pull requests to a specific Silex API endpoint. According to the Github best practices (https://developer.github.com/v3/guides/best-practices-for-integrators/#favor-asynchronous-work-over-synchronous):
GitHub expects that integrations respond within 10 seconds of
receiving the webhook payload. If your service takes longer than that
to complete, then GitHub terminates the connection and the payload is
lost.
We should send an answer directly to Github, and process the POST request later.
I've setup my code like this:
$app->post('/github/receive/pullreq', function (\Symfony\Component\HttpFoundation\Request $request) use ($app) {
$body = $request->request->all();
$app->after(function() use ($body) {
$listener = new \Api\Github\Events\PullRequestEvent();
$dispatcher = new \Symfony\Component\EventDispatcher\EventDispatcher();
$dispatcher->addListener('github.pullrequest.made', array($listener, 'sendTestMail'));
$dispatcher->dispatch('github.pullrequest.made');
});
return new \Symfony\Component\HttpFoundation\Response('PR Received', 201);
});
And the listener class like this:
class PullRequestEvent extends Event
{
public function sendTestMail()
{
sleep(60);
// the message
$msg = "First line of text\nSecond line of text";
// use wordwrap() if lines are longer than 70 characters
$msg = wordwrap($msg,70);
// send email
mail("myemail#gmail.com","My subject",$msg);
}
}
I want to send the 201 response to Github first, and then execute the sendTestMail function. Is this possible with Silex events? If yes, how should I set this up?

SignalR - Sending a message to a specific user using (IUserIdProvider) *NEW 2.0.0*

In the latest version of Asp.Net SignalR, was added a new way of sending a message to a specific user, using the interface "IUserIdProvider".
public interface IUserIdProvider
{
string GetUserId(IRequest request);
}
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}
My question is: How do I know to whom I am sending my message? The explanation of this new method is very superficial. And the draft Statement of SignalR 2.0.0 with this bug and does not compile. Has anyone implemented this feature?
More Info : http://www.asp.net/signalr/overview/signalr-20/hubs-api/mapping-users-to-connections#IUserIdProvider
Hugs.
SignalR provides ConnectionId for each connection. To find which connection belongs to whom (the user), we need to create a mapping between the connection and the user. This depends on how you identify a user in your application.
In SignalR 2.0, this is done by using the inbuilt IPrincipal.Identity.Name, which is the logged in user identifier as set during the ASP.NET authentication.
However, you may need to map the connection with the user using a different identifier instead of using the Identity.Name. For this purpose this new provider can be used with your custom implementation for mapping user with the connection.
Example of Mapping SignalR Users to Connections using IUserIdProvider
Lets assume our application uses a userId to identify each user. Now, we need to send message to a specific user. We have userId and message, but SignalR must also know the mapping between our userId and the connection.
To achieve this, first we need to create a new class which implements IUserIdProvider:
public class CustomUserIdProvider : IUserIdProvider
{
public string GetUserId(IRequest request)
{
// your logic to fetch a user identifier goes here.
// for example:
var userId = MyCustomUserClass.FindUserId(request.User.Identity.Name);
return userId.ToString();
}
}
The second step is to tell SignalR to use our CustomUserIdProvider instead of the default implementation. This can be done in the Startup.cs while initializing the hub configuration:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var idProvider = new CustomUserIdProvider();
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
}
}
Now, you can send message to a specific user using his userId as mentioned in the documentation, like:
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}
Here's a start.. Open to suggestions/improvements.
Server
public class ChatHub : Hub
{
public void SendChatMessage(string who, string message)
{
string name = Context.User.Identity.Name;
Clients.Group(name).addChatMessage(name, message);
Clients.Group("2#2.com").addChatMessage(name, message);
}
public override Task OnConnected()
{
string name = Context.User.Identity.Name;
Groups.Add(Context.ConnectionId, name);
return base.OnConnected();
}
}
JavaScript
(Notice how addChatMessage and sendChatMessage are also methods in the server code above)
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call to broadcast messages.
chat.client.addChatMessage = function (who, message) {
// Html encode display name and message.
var encodedName = $('<div />').text(who).html();
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#chat').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedMsg + '</li>');
};
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.sendChatMessage($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
Testing
This is how use SignarR in order to target a specific user (without using any provider):
private static ConcurrentDictionary<string, string> clients = new ConcurrentDictionary<string, string>();
public string Login(string username)
{
clients.TryAdd(Context.ConnectionId, username);
return username;
}
// The variable 'contextIdClient' is equal to Context.ConnectionId of the user,
// once logged in. You have to store that 'id' inside a dictionaty for example.
Clients.Client(contextIdClient).send("Hello!");
Look at SignalR Tests for the feature.
Test "SendToUser" takes automatically the user identity passed by using a regular owin authentication library.
The scenario is you have a user who has connected from multiple devices/browsers and you want to push a message to all his active connections.
Old thread, but just came across this in a sample:
services.AddSignalR()
.AddAzureSignalR(options =>
{
options.ClaimsProvider = context => new[]
{
new Claim(ClaimTypes.NameIdentifier, context.Request.Query["username"])
};
});
For anyone trying to do this in asp.net core. You can use claims.
public class CustomEmailProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
}
}
Any identifier can be used, but it must be unique. If you use a name identifier for example, it means if there are multiple users with the same name as the recipient, the message would be delivered to them as well. I have chosen email because it is unique to every user.
Then register the service in the startup class.
services.AddSingleton<IUserIdProvider, CustomEmailProvider>();
Next. Add the claims during user registration.
var result = await _userManager.CreateAsync(user, Model.Password);
if (result.Succeeded)
{
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Email, Model.Email));
}
To send message to the specific user.
public class ChatHub : Hub
{
public async Task SendMessage(string receiver, string message)
{
await Clients.User(receiver).SendAsync("ReceiveMessage", message);
}
}
Note: The message sender won't be notified the message is sent. If you want a notification on the sender's end. Change the SendMessage method to this.
public async Task SendMessage(string sender, string receiver, string message)
{
await Clients.Users(sender, receiver).SendAsync("ReceiveMessage", message);
}
These steps are only necessary if you need to change the default identifier. Otherwise, skip to the last step where you can simply send messages by passing userIds or connectionIds to SendMessage. For more

Resources