Skip to content

Commit

Permalink
Laravel 6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
ChinwalPrasad committed Oct 26, 2019
0 parents commit 8b7f385
Show file tree
Hide file tree
Showing 13 changed files with 782 additions and 0 deletions.
39 changes: 39 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "uisits/laravel-shibboleth",
"description": "Enable basic Shibboleth support for Laravel 6.x",
"authors": [
{
"name": "Chinwal Prasad",
"email": "pchin3@uis.edu"
}
],
"require": {
"illuminate/support": "5.* || ^6.0",
"mrclay/shibalike": "1.0.0",
"laravel/framework": "^5.4 || ^6.0",
"tymon/jwt-auth": "1.0.x-dev"
},
"autoload": {
"psr-4": {
"StudentAffairsUwm\\Shibboleth\\": "src/StudentAffairsUwm/Shibboleth"
}
},
"autoload-dev": {
"psr-4": {
"StudentAffairsUwm\\Shibboleth\\Tests\\Stubs\\": "tests/setup/Stubs",
"App\\": "tests/setup/app"
}
},
"minimum-stability": "stable",
"require-dev": {
"phpunit/phpunit": "^6.0",
"orchestra/testbench": "3.4.*"
},
"extra": {
"laravel": {
"providers": [
"StudentAffairsUwm\\Shibboleth\\ShibbolethServiceProvider"
]
}
}
}
31 changes: 31 additions & 0 deletions src/.htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>

RewriteEngine On

# Redirect Trailing Slashes...
RewriteRule ^(.*)/$ /$1 [L,R=301]

# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>

<IfModule mod_shib_22.so>
AuthType shibboleth
Require shibboleth
ShibUseHeaders On
ShibRequireSession Off
ShibRequestSetting isPassive Off
</IfModule>

<IfModule mod_shib_24.so>
AuthType shibboleth
Require shibboleth
ShibUseHeaders On
ShibRequireSession Off
ShibRequestSetting isPassive Off
</IfModule>
251 changes: 251 additions & 0 deletions src/StudentAffairsUwm/Shibboleth/Controllers/ShibbolethController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
<?php

namespace StudentAffairsUwm\Shibboleth\Controllers;

use JWTAuth;
use Illuminate\Auth\GenericUser;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Redirect;

class ShibbolethController extends Controller
{
/**
* Service Provider
* @var Shibalike\SP
*/
private $sp;

/**
* Identity Provider
* @var Shibalike\IdP
*/
private $idp;

/**
* Configuration
* @var Shibalike\Config
*/
private $config;

/**
* Constructor
*/
public function __construct(GenericUser $user = null)
{
if (config('shibboleth.emulate_idp') === true) {
$this->config = new \Shibalike\Config();
$this->config->idpUrl = '/emulated/idp';

$stateManager = $this->getStateManager();

$this->sp = new \Shibalike\SP($stateManager, $this->config);
$this->sp->initLazySession();

$this->idp = new \Shibalike\IdP($stateManager, $this->getAttrStore(), $this->config);
}

$this->user = $user;
}

/**
* Create the session, send the user away to the IDP
* for authentication.
*/
public function login()
{
if (config('shibboleth.emulate_idp') === true) {
return Redirect::to(action('\\' . __class__ . '@emulateLogin')
. '?target=' . action('\\' . __class__ . '@idpAuthenticate'));
}

return Redirect::to('https://' . Request::server('SERVER_NAME')
. ':' . Request::server('SERVER_PORT') . config('shibboleth.idp_login')
. '?target=' . action('\\' . __class__ . '@idpAuthenticate'));
}

/**
* Setup authentication based on returned server variables
* from the IdP.
*/
public function idpAuthenticate()
{
if (empty(config('shibboleth.user'))) {
throw new \Exception('No user attribute mapping for server variables.');
}

foreach (config('shibboleth.user') as $local => $server) {
$map[$local] = $this->getServerVariable($server);
}

if (empty($map['email'])) {
return abort(403, 'Unauthorized');
}

$userClass = config('auth.providers.users.model', 'App\User');

// Attempt to login with the email, if success, update the user model
// with data from the Shibboleth headers (if present)
if (Auth::attempt(array('email' => $map['email']), true)) {
$user = $userClass::where('email', '=', $map['email'])->first();

// Update the model as necessary
$user->update($map);
}

// Add user and send through auth.
elseif (config('shibboleth.add_new_users', true)) {
$map['password'] = 'shibboleth';
$user = $userClass::create($map);
Auth::login($user);
} else {
return abort(403, 'Unauthorized');
}

Session::regenerate();

$route = config('shibboleth.authenticated');

if (config('jwtauth') === true) {
$route .= $this->tokenizeRedirect($user, ['auth_type' => 'idp']);
}

return redirect()->intended($route);
}

/**
* Destroy the current session and log the user out, redirect them to the main route.
*/
public function destroy()
{
Auth::logout();
Session::flush();

if (config('jwtauth')) {
$token = JWTAuth::parseToken();
$token->invalidate();
}

if (config('shibboleth.emulate_idp') == true) {
return Redirect::to(action('\\' . __class__ . '@emulateLogout'));
}

return Redirect::to('https://' . Request::server('SERVER_NAME') . config('shibboleth.idp_logout'));
}

/**
* Emulate a login via Shibalike
*/
public function emulateLogin()
{
$from = (Request::input('target') != null) ? Request::input('target') : $this->getServerVariable('HTTP_REFERER');

$this->sp->makeAuthRequest($from);
$this->sp->redirect();
}

/**
* Emulate a logout via Shibalike
*/
public function emulateLogout()
{
$this->sp->logout();

$referer = $this->getServerVariable('HTTP_REFERER');

die("Goodbye, fair user. <a href='$referer'>Return from whence you came</a>!");
}

/**
* Emulate the 'authentication' via Shibalike
*/
public function emulateIdp()
{
$data = [];

if (Request::input('username') != null) {
$username = (Request::input('username') === Request::input('password')) ?
Request::input('username') : '';

$userAttrs = $this->idp->fetchAttrs($username);
if ($userAttrs) {
$this->idp->markAsAuthenticated($username);
$this->idp->redirect(route('shibboleth-authenticate'));
}

$data['error'] = 'Incorrect username and/or password';
}

return view('shibalike::IdpLogin', $data);
}

/**
* Function to get an attribute store for Shibalike
*/
private function getAttrStore()
{
return new \Shibalike\Attr\Store\ArrayStore(config('shibboleth.emulate_idp_users'));
}

/**
* Gets a state manager for Shibalike
*/
private function getStateManager()
{
$session = \UserlandSession\SessionBuilder::instance()
->setSavePath(sys_get_temp_dir())
->setName('SHIBALIKE_BASIC')
->build();

return new \Shibalike\StateManager\UserlandSession($session);
}

/**
* Wrapper function for getting server variables.
* Since Shibalike injects $_SERVER variables Laravel
* doesn't pick them up. So depending on if we are
* using the emulated IdP or a real one, we use the
* appropriate function.
*/
private function getServerVariable($variableName)
{
if (config('shibboleth.emulate_idp') == true) {
return isset($_SERVER[$variableName]) ?
$_SERVER[$variableName] : null;
}

$variable = Request::server($variableName);

return (!empty($variable)) ?
$variable : Request::server('REDIRECT_' . $variableName);
}

/*
* Simple function that allows configuration variables
* to be either names of views, or redirect routes.
*/
private function viewOrRedirect($view)
{
return (View::exists($view)) ? view($view) : Redirect::to($view);
}

/**
* Uses JWTAuth to tokenize the user and returns a URL query string.
*
* @param App\User $user
* @param array $customClaims
* @return string
*/
private function tokenizeRedirect($user, $customClaims)
{
// This is where we used to setup a session. Now we will setup a token.
$token = JWTAuth::fromUser($user, $customClaims);

// We need to pass the token... how?
// Let's try this.
return "?token=$token";
}
}
32 changes: 32 additions & 0 deletions src/StudentAffairsUwm/Shibboleth/Entitlement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
namespace StudentAffairsUwm\Shibboleth;

use InvalidArgumentException;
use Request;

class Entitlement
{
/**
* Returns TRUE if current user has entitlement.
* NOTE: does not work with Shibalike. Only with production Shibboleth.
*
* @param string $entitlement
* @return bool
*/
public static function has($entitlement)
{
if (empty($entitlement)) {
throw new \InvalidArgumentException('Entitlement must not be empty.');
}

$variable = config('shibboleth.entitlement');

foreach (explode(';', Request::server($variable)) as $given) {
if ($given === $entitlement) {
return true;
}
}

return false;
}
}
Loading

0 comments on commit 8b7f385

Please sign in to comment.