Rails 6 ActionMailer previews and http basic authentication - actionmailer

I have the following ActionMailer configuration in production.rb:
unless ENV['APP_NAME'] == 'application-production'
config.action_mailer.preview_path = Rails.root.join('test/mailers/previews')
config.action_mailer.show_previews = true
end
The problem is, non-production apps, such as the staging app, should have http authentication for the whole domain, including the mailer preview path '/rails/mailers' which is now accessible without auth. What's the missing configuration?
EDIT: I tried adding an initializer as per this answer: https://stackoverflow.com/a/44923157/2116456. I would like to use ActionController::Base.http_basic_authenticate_with in the initializer, but it's not available

Okay, I found a way to do what I needed:
I set up an initializer: 'initializers/mailer_previews.rb' with the following:
if Rails.application.config.action_mailer.show_previews
Rails::MailersController.prepend_before_action do
head :forbidden unless authenticate_or_request_with_http_basic('Plz login') do |username, password|
username == ENV['AUTH_NAME'] && password == ENV['AUTH_PASSWORD']
end
end
end
Thanks to #prcu for pointing me in the right direction (see this post)

Related

Rails - How to authorise user with third party api

I'm setting up some authentication in my rails application. Only thing is I want to log in a user based on their credentials with another API.
The application will have to send a POST request with their username and password in the body to the API and if the request is successful then the user authorised.
I'm having trouble trying to do this with devise, I'm just looking for tips you guys have in order to implement this.
Thanks!
Devise allows you to define custom strategies for authentication. You can therefore create a new strategy to handle it. Database Authentication is one of the strategy already defined at Devise. You can check the source here
A rough idea of your strategy could like this.
Create a file at config/initializers/external_authenticatable.rb and define the strategy
require 'devise/strategies/database_authenticatable'
module Devise
module Strategies
class ExternalAuthenticatable < DatabaseAuthenticatable
def authenticate!
resource = password.present? && mapping.to.find_for_database_authentication(authentication_hash)
if validate(resource){ valid_credentials?(resource) }
remember_me(resource)
resource.after_database_authentication
success!(resource)
end
fail(:not_found_in_database) unless resource
end
def valid_credentials?(resource)
request_params = { email: resource.email, password: password }
# Make your post request here and return true false using authentication_hash
end
end
end
end
Now we need to inform devise that we want to use this strategy first before any other defaults. This can be done by editing /config/initializers/devise.rb
config.warden do |manager|
manager.strategies.add(:external, Devise::Strategies::ExternalAuthenticatable)
manager.default_strategies(:scope => :user).unshift :external
end
Restart your Rails application and you are done.

Authentication That Doesn't Require Javascript?

I have a Web API app, initialized thusly:
app.UseCookieAuthentication();
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOAuthBearerTokens(OAuthOptions);
app.UseGoogleAuthentication();
For calls to most controllers, it works great. However, it also requires a bit of javascript before client-side service calls are made:
function getSecurityHeaders() {
var accessToken = sessionStorage["accessToken"] || localStorage["accessToken"];
if (accessToken) {
return { "Authorization": "Bearer " + accessToken };
}
return {};
}
The problem is that we have a certain type of controller (one that accesses files) where no javascript can be run during the call. For example, the call might be to:
http://mysite/mycontroller/file/filename.jpg
...where the value is assigned as the src attribute of an img tag. The call works, but Thread.CurrentPrincipal.Identity is unauthenticated with a null name, so there's currently not a way to enforce security.
I'm new to Web API, so it may be a dumb question, but what's the way around this? What switches do I need to flip to not require javascript to add security headers? I was considering trying to find a way to force an authorization header in an IAuthorizationFilter or something, but I'm not even sure that would work.
So I figured out the solution to my problem.
First, I needed to configure the app to use an authentication type of external cookies thusly:
//the line below is the one I needed to change
app.UseCookieAuthentication(AuthenticationType = DefaultAuthenticationTypes.ExternalCookie);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOAuthBearerTokens(OAuthOptions);
app.UseGoogleAuthentication();
Second, it turned out there was a line of code in my WebApiConfig file that was disabling reading the external cookie:
//this line needed to be removed
//config.SuppressDefaultHostAuthentication();
After that, I could see the external cookie from Google, which passed along an email address I could identify the user with.

OAuth & facebook access_token not working, need OAuth expert

I have read the 1000+ blogs about how the redirect_uri has to be the same in both calls to OAuth in order to get a user token, but 100% of the time, regardless of how I format the URL, it fails with:
{
"error": {
"message": "Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request",
"type": "OAuthException",
"code": 100
}
}
I have been meticulous in making sure that the URLs in both calls were the exact same. My URL has to have a ? in it and I have tried replacing it with %3f but that didn't help. There has to be something else that can cause this error, I need to learn what that might be?
This seemed to break for me over the past month sometime. We did a show in late July and things worked fine (had a different base URL for that show since it was a different server). Could it be that the URL is of this format:
someprestuff.morestuff.mainurl.com?prm=value
Are there too many "parts" to the URL for Facebook to accept it?
I'm looking for alternate things to look for.
The url should be the same and it has to be escaped. In the url it has to look like this:
http%3A//someprestuff.morestuff.mainurl.com%3Fprm%3Dvalue
Jim's comment above worked, but to clarify, it was a forward slash that fixed it for us.
Had the same problem today, the problem turned out to be that the redirect_uri used a http:// URL Schema, and Facebook only accepts https://
I just finished tracing this issue on a server that was behind a load balancer. It turns out that while the load balancer was passing the HTTP_X_FORWARDED_PROTO header, somehow between the V3 PHP Facebook library, PHP and Apache, the header was not being recognized.
Here is the relevant code from the library:
protected function getHttpProtocol() {
if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
return 'https';
}
return 'http';
}
/*apache + variants specific way of checking for https*/
if (isset($_SERVER['HTTPS']) &&
($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] == 1)) {
return 'https';
}
/*nginx way of checking for https*/
if (isset($_SERVER['SERVER_PORT']) &&
($_SERVER['SERVER_PORT'] === '443')) {
return 'https';
}
return 'http';
}
As you can see, there are a few scenarios being accommodated here, so you'll have to ensure that whatever situation applies to you is properly configured for this block of code to succeed.
The most likely solution will be to set trustForwarded to true in your facebook config array.

Nginx module redirect

Good day
I am writing a module for Nginx, that should redirect user to certain (local) URLs, if several conditions apply - something like ngx_http_rewrite_module (Though I couldn't find redirection code in that module).
My code successfully runs on required events, but I am unable to redirect user to another page.
I've tried ngx_http_internal_redirect, but it didn't work:
static ngx_str_t ngx_redirect_script = ngx_string("/dst.php");
return ngx_http_internal_redirect(r, &ngx_redirect_script , &r->args);
Perhaps somebody knows how to do that?
Thank you!
Interesting, this worked:
ngx_http_internal_redirect(r, &ngx_redirect_script , &r->args);
return NGX_HTTP_OK;

PHPUnit and Zend Framework assertRedirectTo() issue

I'm having an issue with assertRedirectTo() in a test I have created, below is the code I have used:
public function testLoggedInIndexAction() {
$this->dispatch('/');
$this->assertController('index');
$this->resetResponse();
$this->request->setPost(array(
'type' => 'login',
'username' => 'root',
'password' => 'asdasd',
));
$this->request->setMethod('POST');
$this->dispatch('/');
$this->assertRedirectTo('/feed/');
}
You log in through / (index.php/) and submit post details there and the it redirects you to /feed/ (index.php/feed/). The details I have supplied are correct and should work however I am having issues whereby PHPUnit is saying they are incorrect:
There was 1 failure:
1) IndexControllerTest::testLoggedInIndexAction
Failed asserting response redirects to "/feed/"
/home/public_html/mashhr/library/Zend/Test/PHPUnit/Constraint/Redirect.php:190
/home/public_html/mashhr/library/Zend/Test/PHPUnit/ControllerTestCase.php:701
/home/public_html/mashhr/tests/application/controllers/UserControllerTest.php:36
#poelinca: No, it is simply a case of Zend_Test being unreliable in registering a redirect (even if it has been called correctly!)
In his case, the real app is no doubt redirecting the user properly, but the Zend_Test environment is having trouble registering properly called redirects. The best response I can think of is to omit any failing assertRedirect which actually works in the application.
This is not an optimal situation, but unless you're prepared to dig into the Zend code to see where the problem is, this may be your best bet for efficiency. This is an example of what causes unit testing to get a bad name: Having to alter code to pass tests which actually work already.
See http://framework.zend.com/issues/browse/ZF-7496 Which is misleadingly specific in its title: the problem relates to all redirects, especially those which must set headers and exit instead of dispatching the original controller.
For whatever reason, this behavior causes Redirects not to always fail, but to be highly unreliable instead! If anyone knows a better workaround to this problem (which is general, and probably unrelated to the OP's code) please let us know.
stumbled on this question while having the same problem. I ended up doing the following:
$this->assertRedirect();
$responseHeaders = $this->response->getHeaders();
$this->assertTrue(count($responseHeaders) != 0);
$this->assertArrayHasKey('value', $responseHeaders[0]);
// in my case I'm redirecting to another module
$this->assertEquals('/module/controller/action', $responseHeaders[0]['value']);
I've responded this answer in http://zend-framework-community.634137.n4.nabble.com/Zend-Test-failing-on-AssertRedirectTo-td3325845.html#a4451217
I'm having this same issue... A possible way to assert it could be in
PHPUnit and Zend Framework assertRedirectTo() issue
But the problem is there.. My example is (wich actually works if done manually):
// controller modules/picking/orders/product
$orderId = $this->_getParam('oId');
if (empty($orderId)) {
return $this->_redirect('picking/orders/browse/invalid/empty');
}
// test
$this->dispatch('picking/orders/product');
$this->assertRedirect(); // ok
$this->assertRedirectTo('picking/orders/browse'); // error
$this->assertRedirectTo('picking/orders/browse/invalid/empty'); // error
After I've found the error!
Actually, following the example above i've found that the string comparizon in my example has an error:
'.../public//picking/orders/browse/invalid/empty'
'.../public/picking/orders/browse/invalid/empty'
... fixing the preprended slash solve the problem! ;)
So if i understand right , you wrote a test that fails ( id say this is perfect ) .
In order to make the test pass you need to debug you're app and see where the problem is , in this case you need to have a look at the actual redirection ( or eaven the post fields sent by the form ) , maybe check the routing too . I gues nobody here will be able to answer you're question unless you post the code in you're index controller/form/view and feed controller .
For future reference, I had this issue today and it was caused by the Url class failing to build a valid url (I was passing the wrong parameters) but not reporting an error to PHPUnit (probably because PHPUnit masks the error).
Correcting the url parameters fixes the url and with it the assertion.