Showing posts with label Symfony. Show all posts
Showing posts with label Symfony. Show all posts

Saturday, April 6, 2019

Handle HTTP 302 response from proxy in Angular

HTTP 302 responses, aka redirects, are not available to Angular as xmlHttpRequest does not support returning redirects; the browser acts before you can do anything about it.


If the response is an HTTP redirect:

If the origin of the URL conveyed by the Location header is same origin with the XMLHttpRequest origin and the redirect does not violate infinite loop precautions, transparently follow the redirect while observing the same-origin request event rules.
refernce: stackoverflow


To act upon  a 302 response, 

You could check for specific data responses, or worse, override the status code 302 with a status code that is returned, such as 403, and act upon that.


X-Redirect header

However, a more generic solution is to add a custom redirect header, such as `X-Redirect` and act upon that. The value of `X-Redirect` should be the url you want to redirect to eg https://other.url.com/page

An $http example:
$http.get(orig_url).then(function(response) {
        // do stuff
    }, function (response) {
        var headers = response.headers();
        if ('x-redirect' in headers)
        {
            document.location.href = headers['x-redirect'];
        }
        return response;
    }
}

For a more global solution, you could also use a HTTP Interceptor:
app.factory('myHttpInterceptor', function ($q, myCache) {
    return {
        request: function (config) {
            return config;
        },
        response: function(response){
            var headers = response.headers();
            if ('x-redirect' in headers)
            {
                document.location.href = headers['x-redirect'];
            }    
            return response;
        },
        responseError: function(rejection) {    
            switch(rejection.status)
            {
                case 302:
                    // this will not occur, use the custom header X-Redirect instead
                    break;
                case 403:
                    alert.error(rejection.data, 'Forbidden');
                    break;
            }
            return $q.reject(rejection);
        }
    };
});

You can set the custom header server side.
For example, if you are using PHP Symfony:

$response = new Response('', 204);
$response->headers->set('X-Redirect', $this->generateUrl('other_url'));
return $response;

-End of Document-
Thanks for reading

Saturday, January 26, 2019

https with ngrok

While ngrok can be used to expose your local development application to the internet for testing,
the free version of ngrok does not support End-to-End TLS Tunnels aka https, but it does support forwarding of https to http

Note: Transport Layer Security (TLS) is the successor to SSL. TLS 1.0 was defined in RFC 2246 in January 1999.  Hypertext Transfer Protocol Secure (HTTPS), or “HTTP Secure,” is an application-specific implementation that is a combination of the Hypertext Transfer Protocol (HTTP) with the SSL/TLS.
reference: is-it-ssl-tls-or-https

For Single Sing On solutions, such as SAML, you application is required to be hosted over https

You might think you could just bind to the ssl port 443

> ngrok http myawesomeapp.local:443

Session Status                online
Session Expires               7 hours, 4 minutes
Version                       2.2.8
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://8316dcb8.ngrok.io -> myawesomeapp.local:443
Forwarding                    https://8316dcb8.ngrok.io -> myawesomeapp.local:443  

But, accessing https://8316dcb8.ngrok.io or http://8316dcb8.ngrok.io results in the browser showing

Bad Request
Your browser sent a request that this server could not understand.
Reason: You're speaking plain HTTP to an SSL-enabled server port.

So that is why you need the paid version of ngrok for End-to-End TLS Tunnels.

One solution is to pay for the Pro version of ngrok, which also gets you access to more features such as Whitelabel domains, Reserved TCP addresses, End-to-End TLS Tunnels, and more resources.

However, if your using PHP Symfony or the PHP OneLogin SAML library,
another solution is to modify your application to indicate that the request is actually https.

Start up ngrok

> ngrok http myawesomeapp.local:80

Session Status                online
Session Expires               7 hours, 4 minutes
Version                       2.2.8
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://8316dcb8.ngrok.io -> myawesomeapp.local:80
Forwarding                    https://8316dcb8.ngrok.io -> myawesomeapp.local:80  

If you view https://8316dcb8.ngrok.io, you will see your app, but you app will think its running on port 80 and http

Note: While this is PHP focused, the concept should apply to other applications.

To make your app think it's on https, in the bootstrap or initialization of your application, add:

// If the host url has ngrok.io, and the header X-Forwarded-Proto set by ngrok is https, then also set https on and the port to 443
if (strpos($_SERVER['HTTP_HOST'], 'ngrok.io') !== false && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
{
  $_SERVER['HTTPS'] = 'on';
  $_SERVER['SERVER_PORT'] = 443;
}

Note: For Symfony, you can place this code within the boostrap app_dev.php, above the creation of $request

> vi app_dev.php

// place here

$request = Request::createFromGlobals();
$response = $kernel->handle($request);


This will allow any following code which checks for https
such as OneLogins Utils::isHTTPS()
or Symfony Request->isSecure()
to return true when accessing the ngrok url over https

Note: This does not test or affect your applications ssl certs, as the browser thinks the request is from 8316dcb8.ngrok.io,  And if you are using ngrok daily for development or testing, it is fairly inexpensive to upgrade to the tls version which is a  simpler and better solution

Hopefully this helps you test Single Sing On solutions, such as SAML.

-End of Document-
Thanks for reading

Monday, March 19, 2018

Symfony custom database exception

For Symfony2, when your database is down, the default server error 500 message is shown.
To instead show a more appropriate 503 service unavailable message, you need to create custom exception listener. While the custom exception listener will listen to all exceptions, you can specify exception type checks inside it.

In your services.yml you need to specify the listener:
> src\Acme\AppBundle\Resources\config\services.yml

services:
    kernel.listener.your_pdo_listener:
        class: Acme\AppBundle\EventListener\YourExceptionListener
        tags:
           - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }

Now you need to create the listener class:
> src\Acme\AppBundle\EventListener\AcmeAppBundleListener.php

use Symfony\Component\HttpKernel\Event\GetResponseEvent,
    Symfony\Component\HttpKernel\KernelEvents;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;

use PropelException;
// use DoctrineException;

class YourExceptionListener implements EventSubscriberInterface
{
        $env = $this->container->getParameter('kernel.environment');
        if ($env == 'dev' || $env == 'test') {
            // show default exception
            return;
        }

        $exception = $event->getException();

        // only customize propel or doctrine ie database exceptions
        // if (!$exception instanceof DoctrineException) {
        if (!$exception instanceof PropelException) {
            // show default exception
            return;
        }     

        $path = $event->getRequest()->getPathInfo();

        $response = new Response();

        // set status text
        $exception_msg = $exception->getMessage();
        if (stripos($exception_msg, 'Unable to open PDO connection') !== false) {
            $response->setStatusCode(Response::HTTP_SERVICE_UNAVAILABLE);
            $status_text = $this->container->getParameter("message.error.unable_to_connect");
        } else {
            // show default exception
            return;
        }

        // add exception code, if set
        $exception_code = $exception->getCode();
        if ($exception_code > 0) {
            $status_text .= " [".$exception_code."]";
        }

        $response->setContent(
            $this->templating->render(
                'AcmeAppBundle:Exception:exception.html.twig',
                [
                    'status_code' => $response->getStatusCode(),
                    'status_text' => $status_text
                ]
            )
        );

        $event->setResponse($response);
}

Add the message configuration:
> src\Acme\AppBundle\Resources\config\messages.yml

parameters:
    message.error.unable_to_connect:        "Unable to connect to service at the moment"


Create the exception template:
> src\Acme\AppBundle\Resources\views\Exception\exception.html.twig

<!DOCTYPE html>
<html>
<head>
    <meta charset="{{ _charset }}" />
    <title>An Error Occurred: {{ status_text }}</title>
</head>
<body>
<h1>Zork! An Error Occurred</h1>
<h2>The server returned a "{{ status_code }} {{ status_text }}".</h2>

<div>
    This should be temporary, but if not, please let us know what you were doing when this error occurred.
    We will fix it as soon as possible. Sorry for any inconvenience caused.
</div>
</body>
</html>

Stop your database or in your code throw DoctrineException or PropelException and you should see the new exception page.

Reference
https://stackoverflow.com/questions/29732630/catching-database-exceptions-in-symfony2

End of document. Thanks for reading.