...
 
Commits (10)
vendor/
\ No newline at end of file
# redirecttoken-laravel
Laravel integration for the redirecttoken PHP package
\ No newline at end of file
Laravel integration for the redirecttoken PHP package
## Description
This package implements the redirecttoken (https://gitlab.com/alasdairkeyes/redirecttoken) for the Laravel framework.
This redirects website visitors to the URI with a 302 redirect. If an invalid request is received a 400 response is returned.
## Installation
## Configuration
* Publish the config
`php artisan vendor:publish --provider="RedirectToken\Laravel\Providers\RedirectTokenServiceProvider" --tag=config`
* Update your environment with a secret at least 10 characters long. This is a minimum length, the longer the string, the better.
`REDIRECT_TOKEN_SECRET=MyIncrediblySecureSecret`
* If you're using config caching, refresh your cache
`php artisan config:cache`
**If you're using Laravel >=5.5 you can stop here. The rest of the setup will auto-load.**
For Lumen and Laravel <5.5 do the following...
* Add the Service Provider
Add the Class to your `app/config.php` config file
```
'providers' => [
// Other providers above...
RedirectToken\Laravel\Providers\RedirectTokenServiceProvider::class,
// Other providers below...
],
```
* Add the Aliases
Add the Alias to your `app/config.php` config file
```
'aliases' => [
// Other aliases above...
'RedirectTokenHelper' => RedirectToken\Laravel\Support\RedirectTokenHelper::class,
// Other aliases below...
],
```
## Example
In your template just add the following directive into your `href` value
`<a href="@redirectUrl('https://www.somewebsite.com')">Redirect To Some Website</a>`
## Extending functionality
The following settings can be updated by updating your `.env` file
### Forwarding Path
By default the forwarding path in your application is `/fwd`. This can be changed by using the following
`REDIRECT_TOKEN_PATH=/forwarder`
### Hashing Algorithm
This uses the `sha256` hashing algorithm to generate the token. This can be changed with
`REDIRECT_TOKEN_HASH_ALGO=sha512`
### Query keys
The default query string generated uses the query parameters `uri` and `token`
This can be changed with
`REDIRECT_TOKEN_URI_QUERY_KEY=redirectto`
`REDIRECT_TOKEN_TOKEN_QUERY_KEY=checksum`
### Custom error page
You can customize the page returned when an invalid redirect request is received by creating a `resources/views/errors/400.blade.php`
### Events
The following events are used
* When a valid redirect is received - `RedirectToken\Laravel\Event\ValidRedirectRequest`
* When an invalid redirect is received `RedirectToken\Laravel\Event\InvalidRedirectRequest`
You can listen and handle these events by adding the following to the `$listen` variable in `app/Providers/EventServiceProvider.php`
```
protected $listen = [
'RedirectToken\Laravel\Event\ValidRedirectRequest' => [
'App\Listeners\ValidRedirect',
]
];
```
Some example listeners are included and can be added to your app with the following command
`php artisan vendor:publish --provider="RedirectToken\Laravel\Providers\RedirectTokenServiceProvider" --tag=listeners`
## Site
https://gitlab.com/alasdairkeyes/redirecttoken
## Author
* Alasdair Keyes - https://akeyes.co.uk/
## License
Released under GPL V3 - See included license file.
\ No newline at end of file
{
"name": "alasdairkeyes/redirecttoken-laravel",
"description": "Laravel integration for the redirecttoken package",
"type": "library",
"homepage": "https://github.com/alasdairkeyes/redirecttoken-laravel",
"license": "GPL-3.0",
"authors": [
{
"name": "Alasdair Keyes",
"email": "alasdair@akeyes.co.uk",
"homepage": "https://www.akeyes.co.uk/",
"role": "Developer"
}
],
"require": {
"alasdairkeyes/redirecttoken": "^1.0",
"guzzlehttp/psr7": "^1.5"
},
"autoload": {
"files": [
],
"psr-4": {
"RedirectToken\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Test\\": "test/"
}
},
"extra": {
"laravel": {
"providers": [
"RedirectToken\\Laravel\\Providers\\RedirectTokenServiceProvider"
],
"aliases": {
"RedirectTokenHelper": "RedirectToken\\Laravel\\Support\\RedirectTokenHelper"
}
}
},
"require-dev": {
"phpunit/phpunit": "^6.5",
"orchestra/testbench": "^3.5",
"laravel/laravel": "^5.5",
"squizlabs/php_codesniffer": "^3.4",
"phpmd/phpmd": "^2.6"
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
<?php
return [
/*
|--------------------------------------------------------------------------
| Redirect Token Secret
|--------------------------------------------------------------------------
|
| This is the key used for creating the hash for URI forwarding validation
|
*/
'secret' => env('REDIRECT_TOKEN_SECRET'),
/*
|--------------------------------------------------------------------------
| Redirect Token Hashing Algorithm
|--------------------------------------------------------------------------
|
| This is the key used for creating the hash for URl forwarding validation
|
*/
'hash_algo' => env('REDIRECT_TOKEN_HASH_ALGO', 'sha256'),
/*
|--------------------------------------------------------------------------
| Redirect Token Path
|--------------------------------------------------------------------------
|
| This is the url that is used to process the redirection
|
*/
'path' => env('REDIRECT_TOKEN_PATH', '/fwd'),
/*
|--------------------------------------------------------------------------
| Redirect Token URI Query Key
|--------------------------------------------------------------------------
|
| This is the GET query key used to specify the URI to redirect to
|
*/
'uri_query_key' => env('REDIRECT_TOKEN_URI_QUERY_KEY', 'uri'),
/*
|--------------------------------------------------------------------------
| Redirect Token Token Query Key
|--------------------------------------------------------------------------
|
| This is the GET query key used to specify the URI to redirect to
|
*/
'token_query_key' => env('REDIRECT_TOKEN_TOKEN_QUERY_KEY', 'token'),
];
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false">
<logging>
<log type="coverage-text" target="php://stdout" showUncoveredFiles="true"/>
</logging>
<testsuites>
<testsuite name="unit-tests">
<directory>./test</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
<php>
<env name="APP_KEY" value="base64:BleAuOrKCfFgtnn8tv1DnoQLYT6fCzEeXY7U+cjQGNQ="/>
</php>
</phpunit>
<?php
/**
* Define routes for RedirectToken
*
* Route is specific to the web routes only
*/
Route::group(
[
'middleware' => ['web'],
'namespace' => '\RedirectToken\Laravel\Http\Controllers'
],
function () {
Route::get(config('redirecttoken.path'), 'RedirectTokenController@index');
}
);
#!/bin/bash
# Move to route of the repo
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd ${DIR}/../
vendor/bin/phpmd src/ text cleancode,codesize,controversial,design,unusedcode,naming
#!/bin/bash
# Move to route of the repo
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd ${DIR}/../
vendor/bin/phpcs --standard=psr2 src/
<?php
namespace RedirectToken\Laravel;
/**
* Enum Class
*
* Enum class provides constants for use in the package
*/
class Enum
{
/**
* Service Container Key for validator
*
* @const string
*/
const GENERATOR_KEY = 'redirectTokenGenerator';
/**
* Service Container Key for validator
*
* @const string
*/
const VALIDATOR_KEY = 'redirectTokenValidator';
}
<?php
namespace RedirectToken\Laravel\Event;
use Illuminate\Http\Request;
class InvalidRedirectRequest
{
/** @var string $uriString */
public $uriString;
/** @var string $token */
public $token;
/** @var Request $request */
public $request;
/**
* Take a requested URL and hash and forward if successful
*
* @param string $uriString
* @param string $token
* @param \Illuminate\Http\Request $request
*/
public function __construct($uriString, $token, Request $request)
{
$this->uriString = $uriString;
$this->token = $token;
$this->request = $request;
}
}
<?php
namespace RedirectToken\Laravel\Event;
use Illuminate\Http\Request;
class ValidRedirectRequest
{
/** @var string $uriString */
public $uriString;
/** @var string $token */
public $token;
/** @var Request $request */
public $request;
/**
* Take a requested URL and hash and forward if successful
*
* @param string $uriString
* @param string $token
* @param \Illuminate\Http\Request $request
*/
public function __construct($uriString, $token, Request $request)
{
$this->uriString = $uriString;
$this->token = $token;
$this->request = $request;
}
}
<?php
namespace RedirectToken\Laravel\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use RedirectToken\Laravel\Support\RedirectTokenHelper;
use RedirectToken\Laravel\Enum;
use RedirectToken\Laravel\Event\ValidRedirectRequest;
use RedirectToken\Laravel\Event\InvalidRedirectRequest;
/**
* Class RedirectTokenController
* @package App\Controllers
*/
class RedirectTokenController extends Controller
{
/**
* Take a requested URL and hash and forward if successful
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
// Get input params
$uriString = $request->input(config('redirecttoken.uri_query_key'));
$token = $request->input(config('redirecttoken.token_query_key'));
// Validate and redirect
if (RedirectTokenHelper::redirectTokenValidate($uriString, $token)) {
event(new ValidRedirectRequest($uriString, $token, $request));
return redirect()->away($uriString);
}
// Or fail
event(new InvalidRedirectRequest($uriString, $token, $request));
abort(400, 'Invalid redirect');
}
}
<?php
namespace RedirectToken\Laravel\Listeners;
use RedirectToken\Laravel\Event\InvalidRedirectRequest;
use Illuminate\Support\Facades\Log;
/**
* Class InvalidRedirect
* @package App\Listeners
*/
class InvalidRedirect
{
/**
* Handle the event.
*
* @param \RedirectToken\Laravel\Event\InvalidRedirectRequest $event
* @return void
*/
public function handle(InvalidRedirectRequest $event)
{
Log::info(
"Got invalid redirect to "
. $event->uriString
. ' with token ' . $event->token
. ' via ' . $event->request->path()
);
}
}
<?php
namespace RedirectToken\Laravel\Listeners;
use RedirectToken\Laravel\Event\ValidRedirectRequest;
use Illuminate\Support\Facades\Log;
/**
* Class ValidRedirect
* @package App\Listeners
*/
class ValidRedirect
{
/**
* Handle the event.
*
* @param \RedirectToken\Laravel\Event\ValidRedirectRequest $event
* @return void
*/
public function handle(ValidRedirectRequest $event)
{
Log::info(
"Got valid redirect to "
. $event->uriString
. ' with token ' . $event->token
. ' via ' . $event->request->path()
);
}
}
<?php
namespace RedirectToken\Laravel\Providers;
use Illuminate\Support\ServiceProvider;
use RedirectToken\Generator;
use RedirectToken\Validator;
use RedirectToken\Laravel\Enum;
/**
* Class RedirectTokenServiceProvider
* @package App\Providers
*/
class RedirectTokenServiceProvider extends ServiceProvider
{
/**
* Boot the RedirectToken Service
*
* @return void
*/
public function boot()
{
$this->loadRoutesFrom(__DIR__ . '/../../../routes/routes.php');
// Define the config publisher
$this->publishes([
__DIR__ . '/../../../config/redirecttoken.php' => config_path('redirecttoken.php'),
], 'config');
// Define the event listener publisher
$this->publishes([
__DIR__ . '/../../../src/Laravel/Listeners/ValidRedirect.php'
=> app_path('Listeners/ValidRedirect.php'),
__DIR__ . '/../../../src/Laravel/Listeners/InvalidRedirect.php'
=> app_path('Listeners/InvalidRedirect.php'),
], 'listeners');
// Define the redirectUrl() directive
$this->compiler()->directive('redirectUrl', function ($urlToForwardTo) {
return '<?php echo RedirectTokenHelper::redirectTokenGenerateUri('
. $urlToForwardTo . ', '
. "config('redirecttoken.path'), "
. "config('redirecttoken.uri_query_key'), "
. "config('redirecttoken.token_query_key')"
. "); ?>";
});
}
/**
* Get the BladeCompiler from the Template
*
* @return BladeCompiler
*/
protected function compiler()
{
return app('view')->getEngineResolver()->resolve('blade')->getCompiler();
}
/**
* Add RedirectToken objects to service container
*
* @return void
*/
public function register()
{
// Load config data and store
$secret = config('redirecttoken.secret');
$hashAlgo = config('redirecttoken.hash_algo');
$this->app->singleton(Enum::GENERATOR_KEY, function () use ($secret, $hashAlgo) {
return new Generator($secret, $hashAlgo);
});
$this->app->singleton(Enum::VALIDATOR_KEY, function () use ($secret, $hashAlgo) {
return new Validator($secret, $hashAlgo);
});
}
}
<?php
namespace RedirectToken\Laravel\Support;
use GuzzleHttp\Psr7\Uri;
/**
* Helper functions for RedirectToken in Laravel
*/
class RedirectTokenHelper
{
/**
* Helper function to generate a token for a given URI
*
* @param string $urlToForwardTo
* @return string
*/
public static function redirectTokenGenerate($urlToForwardTo)
{
/**
* @var RedirectToken\Generator $generator
*/
$generator = app('redirectTokenGenerator');
$uri = new Uri($urlToForwardTo);
return $generator->generateToken($uri);
}
/**
* Helper function to validate given URI/Token
*
* @param string $urlToForwardTo
* @param string $token
* @return bool
*/
public static function redirectTokenValidate($urlToForwardTo, $token)
{
/**
* @var RedirectToken\Validator $validator
*/
$validator = app('redirectTokenValidator');
$uri = new Uri($urlToForwardTo);
return $validator->validateUriToken($uri, $token);
}
/**
* Generate a the forward path for a URI
*
* @param string $urlToForwardTo
* @return string
*/
public static function redirectTokenGenerateUri($urlToForwardTo)
{
/**
* @var string $token
*/
$token = self::redirectTokenGenerate($urlToForwardTo);
$fwdPath = config('redirecttoken.path');
$uriQueryKey = config('redirecttoken.uri_query_key');
$tokenQueryKey = config('redirecttoken.token_query_key');
return $fwdPath . '?' . http_build_query([ $uriQueryKey => $urlToForwardTo, $tokenQueryKey => $token], '', '&');
}
}
<?php
namespace Test;
/**
* Class BladeDirectiveTest
* @package Test
*/
class BladeDirectiveTest extends TestCase
{
public function testDirective()
{
$compiler = $this->getCompiler();
$actual = $compiler->compileString('@redirectUrl("https:://www.fsf.org/")');
$expected = sprintf(
"<?php echo RedirectTokenHelper::redirectTokenGenerateUri(%s, %s, %s, %s); ?>",
'"https:://www.fsf.org/"',
"config('redirecttoken.path')",
"config('redirecttoken.uri_query_key')",
"config('redirecttoken.token_query_key')"
);
$this->assertSame($expected, $actual);
}
}
<?php
namespace Test;
use Illuminate\Http\Request;
/**
* Class ControllerTest
* @package Tests
*/
class ControllerTest extends TestCase
{
public function testGetRequestValid()
{
$request = $this->get(
self::VALID_REDIRECTION_URI
);
$request->assertRedirect(self::TEST_REDIRECT_DESTINATION);
}
public function testGetRequestInvalidToken()
{
$request = $this->get(
'/fwd?uri=https%3A%2F%2Fwww.gitlab.com%2Falasdairkeyes%2Fredirecttoken&token=junkToken'
);
$request->assertStatus(400);
}
public function testGetRequestInvalidNoTokenParam()
{
$request = $this->get(
'/fwd?uri=https%3A%2F%2Fwww.gitlab.com%2Falasdairkeyes%2Fredirecttoken'
);
$request->assertStatus(400);
}
public function testGetRequestInvalidNoUriParam()
{
$request = $this->get(
'/fwd?token=junkToken'
);
$request->assertStatus(400);
}
public function testGetRequestInvalidNoParams()
{
$request = $this->get(
'/fwd'
);
$request->assertStatus(400);
}
}
<?php
namespace Test;
use RedirectToken\Laravel\Support\RedirectTokenHelper;
/**
* Class HelperTest
* @package Tests
*/
class HelperTest extends TestCase
{
public function testRedirectTokenGenerateHelper()
{
$actual = RedirectTokenHelper::redirectTokenGenerate(self::TEST_REDIRECT_DESTINATION);
$this->assertSame(self::VALID_URL_TOKEN, $actual);
}
public function testRedirectTokenValidateHelperValidToken()
{
$actual = RedirectTokenHelper::redirectTokenValidate(self::TEST_REDIRECT_DESTINATION, self::VALID_URL_TOKEN);
$this->assertTrue($actual);
}
public function testRedirectTokenValidateHelperInvalidToken()
{
$actual = RedirectTokenHelper::redirectTokenValidate(self::TEST_REDIRECT_DESTINATION, self::INVALID_URL_TOKEN);
$this->assertFalse($actual);
}
public function testRedirectTokenGenerateUri()
{
$actual = RedirectTokenHelper::redirectTokenGenerateUri(self::TEST_REDIRECT_DESTINATION);
$this->assertSame(
self::VALID_REDIRECTION_URI,
$actual
);
}
}
<?php
namespace Test;
use RedirectToken\Laravel\Listeners\InvalidRedirect;
use RedirectToken\Laravel\Event\InvalidRedirectRequest;
use Illuminate\Http\Request;
/**
* Class InvalidRedirectListenerTest
* @package Tests
*/
class InvalidRedirectListenerTest extends TestCase
{
public function testInstantiation()
{
$listener = new InvalidRedirect();
$this->assertInstanceOf(
'RedirectToken\Laravel\Listeners\InvalidRedirect',
$listener
);
}
public function testHandle()
{
// Get Requirements and Build up Mock Event
$request = new Request();
$uriString = self::TEST_REDIRECT_DESTINATION;
$token = self::VALID_URL_TOKEN;
$mockEvent = $this->getMockBuilder(InvalidRedirectRequest::class)
->setConstructorArgs([$uriString, $token, $request])
->getMock();
$listener = new InvalidRedirect();
$this->assertNull($listener->handle($mockEvent));
}
}
<?php
namespace Test;
use RedirectToken\Laravel\Event\InvalidRedirectRequest;
use Illuminate\Http\Request;
/**
* Class InvalidRedirectRequestTest
* @package Tests
*/
class InvalidRedirectRequestTest extends TestCase
{
public function testInstantiate()
{
$invalidRedirectEvent = $this->instantiateEvent();
$this->assertInstanceOf(
'RedirectToken\Laravel\Event\InvalidRedirectRequest',
$invalidRedirectEvent
);
}
public function testAttributes()
{
$invalidRedirectEvent = $this->instantiateEvent();
$this->assertEquals(self::TEST_REDIRECT_DESTINATION, $invalidRedirectEvent->uriString);
$this->assertEquals(self::VALID_URL_TOKEN, $invalidRedirectEvent->token);
$this->assertInstanceOf('Illuminate\Http\Request', $invalidRedirectEvent->request);
}
/**
* Instantiate InvalidRedirectRequest Event Object
*
* @return RedirectToken\Laravel\Event\InvalidRedirectRequest
*/
protected function instantiateEvent()
{
$request = new Request();
return new InvalidRedirectRequest(
self::TEST_REDIRECT_DESTINATION,
self::VALID_URL_TOKEN,
$request
);
}
}
<?php
namespace Test;
use Illuminate\View\Compilers\BladeCompiler;
use RedirectToken\Laravel\Providers\RedirectTokenServiceProvider;
use Orchestra\Testbench\TestCase as OrchestraTestCase;
/**
* Class TestCase
* @package Tests
*/
abstract class TestCase extends OrchestraTestCase
{
// Config settings to use for testing
const TEST_SECRET = '0123456789abcdef';
const TEST_HASH_ALGO = 'sha256';
const TEST_TOKEN_PATH = '/fwd';
const TEST_URI_QUERY_KEY = 'uri';
const TEST_TOKEN_QUERY_KEY = 'token';
// URI to forward to for testing
const TEST_REDIRECT_DESTINATION = 'https://www.gitlab.com/alasdairkeyes/redirecttoken';
// Valid token for the above Settings/URI
const VALID_URL_TOKEN = '428c9aaccace42d47e7473035b82e9e6323efb0750a4370511a39cabf8251692';
// Invalid token
const INVALID_URL_TOKEN = 'junkToFailTheTest';
// Valid Redirection URI generated for all above settings
const VALID_REDIRECTION_URI = '/fwd?uri=https%3A%2F%2Fwww.gitlab.com%2Falasdairkeyes%2Fredirecttoken&token=428c9aaccace42d47e7473035b82e9e6323efb0750a4370511a39cabf8251692';
/**
* @var BladeCompiler
*/
protected $compiler;
/**
* @return BladeCompiler
*/
protected function getCompiler()
{
if (!$this->compiler) {
$this->compiler = $this->app->make(BladeCompiler::class);
}
return $this->compiler;
}
/**
* @param \Illuminate\Foundation\Application $app
* @return array
*/
protected function getPackageProviders($app)
{
// Set config here as the standard Orchestra bootstrapping using
// getEnvironmentSetup() doesn't load config in time before
// calling register() on the Service Provider
$this->setConfig();
return [
RedirectTokenServiceProvider::class
];
}
/**
* Set config for plugin
* @return void
*/
protected function setConfig()
{
$app = app();
$app['config']->set('redirecttoken.secret', self::TEST_SECRET);
$app['config']->set('redirecttoken.hash_algo', self::TEST_HASH_ALGO);
$app['config']->set('redirecttoken.path', self::TEST_TOKEN_PATH);
$app['config']->set('redirecttoken.uri_query_key', self::TEST_URI_QUERY_KEY);
$app['config']->set('redirecttoken.token_query_key', self::TEST_TOKEN_QUERY_KEY);
}
}
<?php
namespace Test;
use RedirectToken\Laravel\Listeners\ValidRedirect;
use RedirectToken\Laravel\Event\ValidRedirectRequest;
use Illuminate\Http\Request;
/**
* Class ValidRedirectListenerTest
* @package Tests
*/
class ValidRedirectListenerTest extends TestCase
{
public function testInstantiation()
{
$listener = new ValidRedirect();
$this->assertInstanceOf(
'RedirectToken\Laravel\Listeners\ValidRedirect',
$listener
);
}
public function testHandle()
{
// Get Requirements and Build up Mock Event
$request = new Request();
$uriString = self::TEST_REDIRECT_DESTINATION;
$token = self::VALID_URL_TOKEN;
$mockEvent = $this->getMockBuilder(ValidRedirectRequest::class)
->setConstructorArgs([$uriString, $token, $request])
->getMock();
$listener = new ValidRedirect();
$this->assertNull($listener->handle($mockEvent));
}
}
<?php
namespace Test;
use RedirectToken\Laravel\Event\ValidRedirectRequest;
use Illuminate\Http\Request;
/**
* Class ValidRedirectRequestTest
* @package Tests
*/
class ValidRedirectRequestTest extends TestCase
{
public function testInstantiate()
{
$validRedirectEvent = $this->instantiateEvent();
$this->assertInstanceOf(
'RedirectToken\Laravel\Event\ValidRedirectRequest',
$validRedirectEvent
);
}
public function testAttributes()
{
$validRedirectEvent = $this->instantiateEvent();
$this->assertEquals(self::TEST_REDIRECT_DESTINATION, $validRedirectEvent->uriString);
$this->assertEquals(self::VALID_URL_TOKEN, $validRedirectEvent->token);
$this->assertInstanceOf('Illuminate\Http\Request', $validRedirectEvent->request);
}
/**
* Instantiate InvalidRedirectRequest Event Object
*
* @return RedirectToken\Laravel\Event\InvalidRedirectRequest
*/
protected function instantiateEvent()
{
$request = new Request();
return new ValidRedirectRequest(
self::TEST_REDIRECT_DESTINATION,
self::VALID_URL_TOKEN,
$request
);
}
}