-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 8b7f385
Showing
13 changed files
with
782 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
251
src/StudentAffairsUwm/Shibboleth/Controllers/ShibbolethController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.