mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-25 13:44:06 -08:00
Merge pull request #8023 from johnson-yi/features/saml_auth
Added #542: add saml authentication
This commit is contained in:
commit
c3d8024e1f
|
@ -6,6 +6,7 @@ use App\Http\Controllers\Controller;
|
|||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Services\LdapAd;
|
||||
use App\Services\Saml;
|
||||
use Com\Tecnick\Barcode\Barcode;
|
||||
use Google2FA;
|
||||
use Illuminate\Foundation\Auth\ThrottlesLogins;
|
||||
|
@ -44,26 +45,38 @@ class LoginController extends Controller
|
|||
*/
|
||||
protected $ldap;
|
||||
|
||||
/**
|
||||
* @var Saml
|
||||
*/
|
||||
protected $saml;
|
||||
|
||||
/**
|
||||
* Create a new authentication controller instance.
|
||||
*
|
||||
* @param LdapAd $ldap
|
||||
* @param Saml $saml
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(LdapAd $ldap)
|
||||
public function __construct(LdapAd $ldap, Saml $saml)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
|
||||
Session::put('backUrl', \URL::previous());
|
||||
$this->ldap = $ldap;
|
||||
$this->saml = $saml;
|
||||
}
|
||||
|
||||
function showLoginForm(Request $request)
|
||||
{
|
||||
$this->loginViaRemoteUser($request);
|
||||
$this->loginViaSaml($request);
|
||||
if (Auth::check()) {
|
||||
return redirect()->intended('dashboard');
|
||||
return redirect()->intended('/');
|
||||
}
|
||||
|
||||
if ($this->saml->isEnabled() && Setting::getSettings()->saml_forcelogin == "1" && !($request->has('nosaml') || $request->session()->has('error'))) {
|
||||
return redirect()->route('saml.login');
|
||||
}
|
||||
|
||||
if (Setting::getSettings()->login_common_disabled == "1") {
|
||||
|
@ -73,6 +86,47 @@ class LoginController extends Controller
|
|||
return view('auth.login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Log in a user by SAML
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return User
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function loginViaSaml(Request $request)
|
||||
{
|
||||
$saml = $this->saml;
|
||||
$samlData = $request->session()->get('saml_login');
|
||||
if ($saml->isEnabled() && !empty($samlData)) {
|
||||
try {
|
||||
LOG::debug("Attempting to log user in by SAML authentication.");
|
||||
$user = $saml->samlLogin($samlData);
|
||||
if(!is_null($user)) {
|
||||
Auth::login($user, true);
|
||||
} else {
|
||||
$username = $saml->getUsername();
|
||||
LOG::debug("SAML user '$username' could not be found in database.");
|
||||
$request->session()->flash('error', trans('auth/message.signin.error'));
|
||||
$saml->clearData();
|
||||
}
|
||||
|
||||
if ($user = Auth::user()) {
|
||||
$user->last_login = \Carbon::now();
|
||||
$user->save();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
LOG::debug("There was an error authenticating the SAML user: " . $e->getMessage());
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log in a user by LDAP
|
||||
*
|
||||
|
@ -309,17 +363,40 @@ class LoginController extends Controller
|
|||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$saml = $this->saml;
|
||||
$sloRedirectUrl = null;
|
||||
$sloRequestUrl = null;
|
||||
|
||||
if ($saml->isEnabled()) {
|
||||
$auth = $saml->getAuth();
|
||||
$sloRedirectUrl = $request->session()->get('saml_slo_redirect_url');
|
||||
|
||||
if (!empty($auth->getSLOurl()) && $settings->saml_slo == '1' && $saml->isAuthenticated() && empty($sloRedirectUrl)) {
|
||||
$sloRequestUrl = $auth->logout(null, array(), $saml->getNameId(), $saml->getSessionIndex(), true, $saml->getNameIdFormat(), $saml->getNameIdNameQualifier(), $saml->getNameIdSPNameQualifier());
|
||||
}
|
||||
|
||||
$saml->clearData();
|
||||
}
|
||||
|
||||
if (!empty($sloRequestUrl)) {
|
||||
return redirect()->away($sloRequestUrl);
|
||||
}
|
||||
|
||||
$request->session()->forget('2fa_authed');
|
||||
|
||||
Auth::logout();
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
if (!empty($sloRedirectUrl)) {
|
||||
return redirect()->away($sloRedirectUrl);
|
||||
}
|
||||
|
||||
$customLogoutUrl = $settings->login_remote_user_custom_logout_url ;
|
||||
if ($settings->login_remote_user_enabled == '1' && $customLogoutUrl != '') {
|
||||
return redirect()->away($customLogoutUrl);
|
||||
}
|
||||
|
||||
return redirect()->route('login')->with('success', trans('auth/message.logout.success'));
|
||||
return redirect()->route('login')->with(['success' => trans('auth/message.logout.success'), 'loggedout' => true]);
|
||||
}
|
||||
|
||||
|
||||
|
|
140
app/Http/Controllers/Auth/SamlController.php
Normal file
140
app/Http/Controllers/Auth/SamlController.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\Saml;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* This controller provides the endpoint for SAML communication and metadata.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
class SamlController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var Saml
|
||||
*/
|
||||
protected $saml;
|
||||
|
||||
/**
|
||||
* Create a new authentication controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Saml $saml)
|
||||
{
|
||||
$this->saml = $saml;
|
||||
|
||||
$this->middleware('guest', ['except' => ['metadata','sls']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SAML SP metadata for Snipe-IT
|
||||
*
|
||||
* /saml/metadata
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function metadata(Request $request)
|
||||
{
|
||||
$metadata = $this->saml->getSPMetadata();
|
||||
|
||||
if (empty($metadata)) {
|
||||
return response()->view('errors.403', [], 403);
|
||||
}
|
||||
|
||||
return response($metadata)->header('Content-Type', 'text/xml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin the SP-Initiated SSO by sending AuthN to the IdP.
|
||||
*
|
||||
* /login/saml
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
$auth = $this->saml->getAuth();
|
||||
$ssoUrl = $auth->login(null, array(), false, false, false, false);
|
||||
return redirect()->away($ssoUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives, parses the assertion from IdP and flashes SAML data
|
||||
* back to the LoginController for authentication.
|
||||
*
|
||||
* /saml/acs
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function acs(Request $request)
|
||||
{
|
||||
$saml = $this->saml;
|
||||
$auth = $saml->getAuth();
|
||||
$auth->processResponse();
|
||||
$errors = $auth->getErrors();
|
||||
|
||||
if (!empty($errors)) {
|
||||
Log::debug("There was an error with SAML ACS: " . implode(', ', $errors));
|
||||
Log::debug("Reason: " . $auth->getLastErrorReason());
|
||||
return redirect()->route('login')->with('error', trans('auth/message.signin.error'));
|
||||
}
|
||||
|
||||
$samlData = $saml->extractData();
|
||||
|
||||
return redirect()->route('login')->with('saml_login', $samlData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives LogoutRequest/LogoutResponse from IdP and flashes
|
||||
* back to the LoginController for logging out.
|
||||
*
|
||||
* /saml/slo
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function sls(Request $request)
|
||||
{
|
||||
$auth = $this->saml->getAuth();
|
||||
$sloUrl = $auth->processSLO(true, null, null, null, true);
|
||||
$errors = $auth->getErrors();
|
||||
|
||||
if (!empty($errors)) {
|
||||
Log::debug("There was an error with SAML SLS: " . implode(', ', $errors));
|
||||
Log::debug("Reason: " . $auth->getLastErrorReason());
|
||||
return view('errors.403');
|
||||
}
|
||||
|
||||
return redirect()->route('logout')->with('saml_slo_redirect_url', $sloUrl);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
|||
use enshrined\svgSanitize\Sanitizer;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Requests\SettingsSamlRequest;
|
||||
use App\Http\Requests\SetupUserRequest;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
|
@ -1002,6 +1003,56 @@ class SettingsController extends Controller
|
|||
return redirect()->back()->withInput()->withErrors($setting->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a form to allow a super admin to update settings.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since v5.0.0
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function getSamlSettings()
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
return view('settings.saml', compact('setting'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves settings from form.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since v5.0.0
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function postSamlSettings(SettingsSamlRequest $request)
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
|
||||
$setting->saml_enabled = $request->input('saml_enabled', '0');
|
||||
$setting->saml_idp_metadata = $request->input('saml_idp_metadata');
|
||||
$setting->saml_attr_mapping_username = $request->input('saml_attr_mapping_username');
|
||||
$setting->saml_forcelogin = $request->input('saml_forcelogin', '0');
|
||||
$setting->saml_slo = $request->input('saml_slo', '0');
|
||||
if (!empty($request->input('saml_sp_privatekey'))) {
|
||||
$setting->saml_sp_x509cert = $request->input('saml_sp_x509cert');
|
||||
$setting->saml_sp_privatekey = $request->input('saml_sp_privatekey');
|
||||
}
|
||||
$setting->saml_custom_settings = $request->input('saml_custom_settings');
|
||||
|
||||
if ($setting->save()) {
|
||||
return redirect()->route('settings.saml.index')
|
||||
->with('success', trans('admin/settings/message.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($setting->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the listing of backups.
|
||||
*
|
||||
|
|
113
app/Http/Requests/SettingsSamlRequest.php
Normal file
113
app/Http/Requests/SettingsSamlRequest.php
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use OneLogin\Saml2\IdPMetadataParser as OneLogin_Saml2_IdPMetadataParser;
|
||||
use OneLogin\Saml2\Utils as OneLogin_Saml2_Utils;
|
||||
|
||||
/**
|
||||
* This handles validating and cleaning SAML settings provided by the user.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
class SettingsSamlRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator($validator)
|
||||
{
|
||||
$validator->after(function ($validator) {
|
||||
if ($this->input('saml_enabled') == '1') {
|
||||
|
||||
$idpMetadata = $this->input('saml_idp_metadata');
|
||||
if (!empty($idpMetadata)) {
|
||||
try {
|
||||
if (filter_var($idpMetadata, FILTER_VALIDATE_URL)) {
|
||||
$metadataInfo = OneLogin_Saml2_IdPMetadataParser::parseRemoteXML($idpMetadata);
|
||||
} else {
|
||||
$metadataInfo = OneLogin_Saml2_IdPMetadataParser::parseXML($idpMetadata);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$validator->errors()->add('saml_idp_metadata', trans('validation.url', ['attribute' => 'Metadata']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->input('saml_sp_regenerate_keypair') == '1' || !$this->has('saml_sp_x509cert')) {
|
||||
$dn = [
|
||||
"countryName" => "US",
|
||||
"stateOrProvinceName" => "N/A",
|
||||
"localityName" => "N/A",
|
||||
"organizationName" => "Snipe-IT",
|
||||
"commonName" => "Snipe-IT",
|
||||
];
|
||||
|
||||
$pkey = openssl_pkey_new([
|
||||
"private_key_bits" => 2048,
|
||||
"private_key_type" => OPENSSL_KEYTYPE_RSA,
|
||||
]);
|
||||
|
||||
$csr = openssl_csr_new($dn, $pkey, ['digest_alg' => 'sha256']);
|
||||
|
||||
$x509 = openssl_csr_sign($csr, null, $pkey, 3650, ['digest_alg' => 'sha256']);
|
||||
|
||||
openssl_x509_export($x509, $x509cert);
|
||||
openssl_pkey_export($pkey, $privateKey);
|
||||
|
||||
$errors = [];
|
||||
while (($error = openssl_error_string() !== false)) {
|
||||
$errors[] = $error;
|
||||
}
|
||||
|
||||
if (!(empty($x509cert) && empty($privateKey))) {
|
||||
$this->merge([
|
||||
'saml_sp_x509cert' => $x509cert,
|
||||
'saml_sp_privatekey' => $privateKey,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->input('saml_custom_settings'))) {
|
||||
$req_custom_settings = preg_split('/\r\n|\r|\n/', $this->input('saml_custom_settings'));
|
||||
$custom_settings = [];
|
||||
|
||||
foreach ($req_custom_settings as $custom_setting) {
|
||||
$split = explode('=', $custom_setting, 2);
|
||||
|
||||
if (count($split) == 2) {
|
||||
$split[0] = trim($split[0]);
|
||||
$split[1] = trim($split[1]);
|
||||
|
||||
if (!empty($split[0])) {
|
||||
$custom_settings[] = implode('=', $split);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->merge(['saml_custom_settings' => implode(PHP_EOL, $custom_settings) . PHP_EOL]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
69
app/Providers/SamlServiceProvider.php
Normal file
69
app/Providers/SamlServiceProvider.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Services\Saml;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class SamlServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->app->singleton(Saml::class, Saml::class);
|
||||
|
||||
Route::group(['namespace'=> 'App\Http\Controllers'], function () {
|
||||
Route::group(['prefix'=> 'saml'], function () {
|
||||
Route::get(
|
||||
'metadata',
|
||||
[
|
||||
'as' => 'saml.metadata',
|
||||
'uses' => 'Auth\SamlController@metadata' ]
|
||||
);
|
||||
|
||||
Route::match(
|
||||
['get', 'post'],
|
||||
'acs',
|
||||
[
|
||||
'as' => 'saml.acs',
|
||||
'uses' => 'Auth\SamlController@acs' ]
|
||||
);
|
||||
|
||||
Route::get(
|
||||
'sls',
|
||||
[
|
||||
'as' => 'saml.sls',
|
||||
'uses' => 'Auth\SamlController@sls' ]
|
||||
);
|
||||
});
|
||||
|
||||
Route::get(
|
||||
'login/saml',
|
||||
[
|
||||
'as' => 'saml.login',
|
||||
'uses' => 'Auth\SamlController@login' ]
|
||||
);
|
||||
|
||||
Route::group(['prefix' => 'admin','middleware' => ['auth', 'authorize:superuser']], function () {
|
||||
|
||||
Route::get('saml', ['as' => 'settings.saml.index','uses' => 'SettingsController@getSamlSettings' ]);
|
||||
Route::post('saml', ['as' => 'settings.saml.save','uses' => 'SettingsController@postSamlSettings' ]);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
}
|
||||
}
|
487
app/Services/Saml.php
Normal file
487
app/Services/Saml.php
Normal file
|
@ -0,0 +1,487 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use OneLogin\Saml2\Auth as OneLogin_Saml2_Auth;
|
||||
use OneLogin\Saml2\IdPMetadataParser as OneLogin_Saml2_IdPMetadataParser;
|
||||
use OneLogin\Saml2\Settings as OneLogin_Saml2_Settings;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* SAML Singleton that builds the settings and loads the onelogin/php-saml library.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
class Saml
|
||||
{
|
||||
const DATA_SESSION_KEY = '_samlData';
|
||||
|
||||
/**
|
||||
* OneLogin_Saml2_Auth instance.
|
||||
*
|
||||
* @var OneLogin\Saml2\Auth
|
||||
*/
|
||||
private $_auth;
|
||||
|
||||
/**
|
||||
* if SAML is enabled and has valid settings.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $_enabled = false;
|
||||
|
||||
/**
|
||||
* Settings to be passed to OneLogin_Saml2_Auth.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_settings = [];
|
||||
|
||||
/**
|
||||
* User attributes data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_attributes = [];
|
||||
|
||||
/**
|
||||
* User attributes data with FriendlyName index.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_attributesWithFriendlyName = [];
|
||||
|
||||
/**
|
||||
* NameID
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_nameid;
|
||||
|
||||
/**
|
||||
* NameID Format
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_nameidFormat;
|
||||
|
||||
/**
|
||||
* NameID NameQualifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_nameidNameQualifier;
|
||||
|
||||
/**
|
||||
* NameID SP NameQualifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_nameidSPNameQualifier;
|
||||
|
||||
/**
|
||||
* If user is authenticated.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $_authenticated = false;
|
||||
|
||||
/**
|
||||
* SessionIndex. When the user is logged, this stored it
|
||||
* from the AuthnStatement of the SAML Response
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_sessionIndex;
|
||||
|
||||
/**
|
||||
* SessionNotOnOrAfter. When the user is logged, this stored it
|
||||
* from the AuthnStatement of the SAML Response
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
private $_sessionExpiration;
|
||||
|
||||
/**
|
||||
* Initializes the SAML service and builds the OneLogin_Saml2_Auth instance.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @throws Exception
|
||||
* @throws Error
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->loadSettings();
|
||||
|
||||
if ($this->isEnabled()) {
|
||||
$this->loadDataFromSession();
|
||||
} else {
|
||||
$this->clearData();
|
||||
}
|
||||
|
||||
try {
|
||||
$this->_auth = new OneLogin_Saml2_Auth($this->_settings);
|
||||
} catch (Exception $e) {
|
||||
$this->_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds settings from Snipe-IT for OneLogin_Saml2_Auth.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function loadSettings()
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
$settings = [];
|
||||
|
||||
$this->_enabled = $setting->saml_enabled == '1';
|
||||
|
||||
if ($this->isEnabled()) {
|
||||
data_set($settings, 'sp.entityId', url('/'));
|
||||
data_set($settings, 'sp.assertionConsumerService.url', route('saml.acs'));
|
||||
data_set($settings, 'sp.singleLogoutService.url', route('saml.sls'));
|
||||
data_set($settings, 'sp.x509cert', $setting->saml_sp_x509cert);
|
||||
data_set($settings, 'sp.privateKey', $setting->saml_sp_privatekey);
|
||||
data_set($settings, 'security.wantAssertionsSigned', true);
|
||||
data_set($settings, 'security.requestedAuthnContext', false);
|
||||
|
||||
if (!empty(data_get($settings, 'sp.privateKey'))) {
|
||||
data_set($settings, 'security.logoutRequestSigned', true);
|
||||
data_set($settings, 'security.logoutResponseSigned', true);
|
||||
}
|
||||
|
||||
$idpMetadata = $setting->saml_idp_metadata;
|
||||
$updatedAt = $setting->updated_at->timestamp;
|
||||
$metadataCache = Cache::get('saml_idp_metadata_cache');
|
||||
try {
|
||||
$url = null;
|
||||
$metadataInfo = null;
|
||||
|
||||
if (empty($metadataCache) || $metadataCache['updated_at'] != $updatedAt) {
|
||||
if (filter_var($idpMetadata, FILTER_VALIDATE_URL)) {
|
||||
$url = $idpMetadata;
|
||||
$metadataInfo = OneLogin_Saml2_IdPMetadataParser::parseRemoteXML($idpMetadata);
|
||||
} else {
|
||||
$metadataInfo = OneLogin_Saml2_IdPMetadataParser::parseXML($idpMetadata);
|
||||
}
|
||||
|
||||
Cache::put('saml_idp_metadata_cache', [
|
||||
'updated_at' => $updatedAt,
|
||||
'url' => $url,
|
||||
'metadata_info' => $metadataInfo,
|
||||
], 604800);
|
||||
} else {
|
||||
$metadataInfo = $metadataCache['metadata_info'];
|
||||
}
|
||||
|
||||
$settings = OneLogin_Saml2_IdPMetadataParser::injectIntoSettings($settings, $metadataInfo);
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
$custom_settings = preg_split('/\r\n|\r|\n/', $setting->saml_custom_settings);
|
||||
if ($custom_settings){
|
||||
foreach($custom_settings as $custom_setting) {
|
||||
$split = explode('=', $custom_setting, 2);
|
||||
|
||||
if (count($split) == 2) {
|
||||
$boolValue = filter_var($split[1], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
|
||||
if (!is_null($boolValue)) {
|
||||
$split[1] = $boolValue;
|
||||
}
|
||||
|
||||
data_set($settings, $split[0], $split[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_settings = $settings;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load SAML data from Session.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function loadDataFromSession() {
|
||||
$samlData = collect(session(self::DATA_SESSION_KEY));
|
||||
$this->_authenticated = !$samlData->isEmpty();
|
||||
$this->_nameid = $samlData->get('nameId');
|
||||
$this->_nameidFormat = $samlData->get('nameIdFormat');
|
||||
$this->_nameidNameQualifier = $samlData->get('nameIdNameQualifier');
|
||||
$this->_nameidSPNameQualifier = $samlData->get('nameIdSPNameQualifier');
|
||||
$this->_sessionIndex = $samlData->get('sessionIndex');
|
||||
$this->_sessionExpiration = $samlData->get('sessionExpiration');
|
||||
$this->_attributes = $samlData->get('attributes');
|
||||
$this->_attributesWithFriendlyName = $samlData->get('attributesWithFriendlyName');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save SAML data to Session.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param string $data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function saveDataToSession($data)
|
||||
{
|
||||
return session([self::DATA_SESSION_KEY => $data]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if SAML is enabled and has valid settings.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear SAML data from session.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearData()
|
||||
{
|
||||
Session::forget(self::DATA_SESSION_KEY);
|
||||
$this->loadDataFromSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find user from SAML data.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param string $data
|
||||
*
|
||||
* @return \App\Models\User
|
||||
*/
|
||||
public function samlLogin($data) {
|
||||
$setting = Setting::getSettings();
|
||||
$this->saveDataToSession($data);
|
||||
$this->loadDataFromSession();
|
||||
|
||||
$username = $this->getUsername();
|
||||
|
||||
return User::where('username', '=', $username)->whereNull('deleted_at')->where('activated', '=', '1')->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OneLogin_Saml2_Auth instance.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return OneLogin\Saml2\Auth
|
||||
*/
|
||||
public function getAuth() {
|
||||
if (!$this->isEnabled()) {
|
||||
throw new HttpException(403, 'SAML not enabled.');
|
||||
}
|
||||
|
||||
return $this->_auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SP metadata. The XML representation.
|
||||
*
|
||||
* @param bool $alwaysPublishEncryptionCert When 'true', the returned
|
||||
* metadata will always include an 'encryption' KeyDescriptor. Otherwise,
|
||||
* the 'encryption' KeyDescriptor will only be included if
|
||||
* $advancedSettings['security']['wantNameIdEncrypted'] or
|
||||
* $advancedSettings['security']['wantAssertionsEncrypted'] are enabled.
|
||||
* @param int|null $validUntil Metadata's valid time
|
||||
* @param int|null $cacheDuration Duration of the cache in seconds
|
||||
*
|
||||
* @return string SP metadata (xml)
|
||||
*/
|
||||
public function getSPMetadata($alwaysPublishEncryptionCert = false, $validUntil = null, $cacheDuration = null)
|
||||
{
|
||||
try {
|
||||
$settings = new OneLogin_Saml2_Settings($this->_settings , true);
|
||||
$metadata = $settings->getSPMetadata($alwaysPublishEncryptionCert, $validUntil, $cacheDuration);
|
||||
|
||||
return $metadata;
|
||||
} catch (Exception $e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract data from SAML Response.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extractData()
|
||||
{
|
||||
$auth = $this->getAuth();
|
||||
return [
|
||||
'attributes' => $auth->getAttributes(),
|
||||
'attributesWithFriendlyName' => $auth->getAttributesWithFriendlyName(),
|
||||
'nameId' => $auth->getNameId(),
|
||||
'nameIdFormat' => $auth->getNameIdFormat(),
|
||||
'nameIdNameQualifier' => $auth->getNameIdNameQualifier(),
|
||||
'nameIdSPNameQualifier' => $auth->getNameIdSPNameQualifier(),
|
||||
'sessionIndex' => $auth->getSessionIndex(),
|
||||
'sessionExpiration' => $auth->getSessionExpiration(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user is authenticated or not.
|
||||
*
|
||||
* @return bool True if the user is authenticated
|
||||
*/
|
||||
public function isAuthenticated()
|
||||
{
|
||||
return $this->_authenticated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of SAML attributes.
|
||||
*
|
||||
* @return array Attributes of the user.
|
||||
*/
|
||||
public function getAttributes()
|
||||
{
|
||||
return $this->_attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of SAML attributes indexed by FriendlyName
|
||||
*
|
||||
* @return array Attributes of the user.
|
||||
*/
|
||||
public function getAttributesWithFriendlyName()
|
||||
{
|
||||
return $this->_attributesWithFriendlyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nameID
|
||||
*
|
||||
* @return string The nameID of the assertion
|
||||
*/
|
||||
public function getNameId()
|
||||
{
|
||||
return $this->_nameid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nameID Format
|
||||
*
|
||||
* @return string The nameID Format of the assertion
|
||||
*/
|
||||
public function getNameIdFormat()
|
||||
{
|
||||
return $this->_nameidFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nameID NameQualifier
|
||||
*
|
||||
* @return string The nameID NameQualifier of the assertion
|
||||
*/
|
||||
public function getNameIdNameQualifier()
|
||||
{
|
||||
return $this->_nameidNameQualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nameID SP NameQualifier
|
||||
*
|
||||
* @return string The nameID SP NameQualifier of the assertion
|
||||
*/
|
||||
public function getNameIdSPNameQualifier()
|
||||
{
|
||||
return $this->_nameidSPNameQualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SessionIndex
|
||||
*
|
||||
* @return string|null The SessionIndex of the assertion
|
||||
*/
|
||||
public function getSessionIndex()
|
||||
{
|
||||
return $this->_sessionIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SessionNotOnOrAfter
|
||||
*
|
||||
* @return int|null The SessionNotOnOrAfter of the assertion
|
||||
*/
|
||||
public function getSessionExpiration()
|
||||
{
|
||||
return $this->_sessionExpiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the correct username from SAML Response.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
$username = $this->getNameId();
|
||||
|
||||
if (!empty($setting->saml_attr_mapping_username))
|
||||
{
|
||||
$attributes = $this->getAttributes();
|
||||
|
||||
if (isset($attributes[$setting->saml_attr_mapping_username])) {
|
||||
$username = $attributes[$setting->saml_attr_mapping_username][0];
|
||||
}
|
||||
}
|
||||
|
||||
return $username;
|
||||
}
|
||||
}
|
|
@ -45,6 +45,7 @@
|
|||
"maknz/slack": "^1.7",
|
||||
"neitanod/forceutf8": "^2.0",
|
||||
"nesbot/carbon": "^2.32",
|
||||
"onelogin/php-saml": "^3.4",
|
||||
"paragonie/constant_time_encoding": "^2.3",
|
||||
"patchwork/utf8": "^1.3",
|
||||
"phpdocumentor/reflection-docblock": "^5.1",
|
||||
|
|
90
composer.lock
generated
90
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "693916c94ecc3dc06b80b9f83d65e4cb",
|
||||
"content-hash": "3fe8a441e49d1299687346810b350e00",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adldap2/adldap2",
|
||||
|
@ -3595,6 +3595,56 @@
|
|||
],
|
||||
"time": "2019-09-05T13:24:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "onelogin/php-saml",
|
||||
"version": "3.4.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/onelogin/php-saml.git",
|
||||
"reference": "5fbf3486704ac9835b68184023ab54862c95f213"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/5fbf3486704ac9835b68184023ab54862c95f213",
|
||||
"reference": "5fbf3486704ac9835b68184023ab54862c95f213",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4",
|
||||
"robrichards/xmlseclibs": ">=3.0.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"pdepend/pdepend": "^2.5.0",
|
||||
"php-coveralls/php-coveralls": "^1.0.2 || ^2.0",
|
||||
"phploc/phploc": "^2.1 || ^3.0 || ^4.0",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1",
|
||||
"sebastian/phpcpd": "^2.0 || ^3.0 || ^4.0",
|
||||
"squizlabs/php_codesniffer": "^3.1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Install curl lib to be able to use the IdPMetadataParser for parsing remote XMLs",
|
||||
"ext-gettext": "Install gettext and php5-gettext libs to handle translations",
|
||||
"ext-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"OneLogin\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "OneLogin PHP SAML Toolkit",
|
||||
"homepage": "https://developers.onelogin.com/saml/php",
|
||||
"keywords": [
|
||||
"SAML2",
|
||||
"onelogin",
|
||||
"saml"
|
||||
],
|
||||
"time": "2019-11-25T17:30:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "opis/closure",
|
||||
"version": "3.5.1",
|
||||
|
@ -4904,6 +4954,44 @@
|
|||
],
|
||||
"time": "2020-02-21T04:36:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "robrichards/xmlseclibs",
|
||||
"version": "3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/robrichards/xmlseclibs.git",
|
||||
"reference": "8d8e56ca7914440a8c60caff1a865e7dff1d9a5a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/8d8e56ca7914440a8c60caff1a865e7dff1d9a5a",
|
||||
"reference": "8d8e56ca7914440a8c60caff1a865e7dff1d9a5a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-openssl": "*",
|
||||
"php": ">= 5.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"RobRichards\\XMLSecLibs\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "A PHP library for XML Security",
|
||||
"homepage": "https://github.com/robrichards/xmlseclibs",
|
||||
"keywords": [
|
||||
"security",
|
||||
"signature",
|
||||
"xml",
|
||||
"xmldsig"
|
||||
],
|
||||
"time": "2020-04-22T17:19:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "rollbar/rollbar",
|
||||
"version": "v2.0.0",
|
||||
|
|
|
@ -339,6 +339,7 @@ return [
|
|||
*/
|
||||
App\Providers\MacroServiceProvider::class,
|
||||
App\Providers\LdapServiceProvider::class,
|
||||
App\Providers\SamlServiceProvider::class,
|
||||
|
||||
|
||||
],
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddSamlFieldsToSettings extends Migration {
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('settings', function(Blueprint $table)
|
||||
{
|
||||
$table->boolean('saml_enabled')->default(0);
|
||||
$table->text('saml_idp_metadata')->nullable()->default(NULL);
|
||||
$table->string('saml_attr_mapping_username')->nullable()->default(NULL);
|
||||
$table->boolean('saml_forcelogin')->default(0);
|
||||
$table->boolean('saml_slo')->default(0);
|
||||
$table->text('saml_sp_x509cert')->nullable()->default(NULL);
|
||||
$table->text('saml_sp_privatekey')->nullable()->default(NULL);
|
||||
$table->text('saml_custom_settings')->nullable()->default(NULL);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('settings', function(Blueprint $table)
|
||||
{
|
||||
$table->dropColumn('saml_enabled');
|
||||
$table->dropColumn('saml_idp_metadata');
|
||||
$table->dropColumn('saml_attr_mapping_username');
|
||||
$table->dropColumn('saml_forcelogin');
|
||||
$table->dropColumn('saml_slo');
|
||||
$table->dropColumn('saml_sp_x509cert');
|
||||
$table->dropColumn('saml_sp_privatekey');
|
||||
$table->dropColumn('saml_custom_settings');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -118,6 +118,20 @@ return array(
|
|||
'pwd_secure_uncommon_help' => 'This will disallow users from using common passwords from the top 10,000 passwords reported in breaches.',
|
||||
'qr_help' => 'Enable QR Codes first to set this',
|
||||
'qr_text' => 'QR Code Text',
|
||||
'saml_enabled' => 'SAML enabled',
|
||||
'saml_integration' => 'SAML Integration',
|
||||
'saml_idp_metadata' => 'SAML IdP Metadata',
|
||||
'saml_idp_metadata_help' => 'You can specify the IdP metadata using a URL or XML file.',
|
||||
'saml_attr_mapping_username' => 'Attribute Mapping - Username',
|
||||
'saml_attr_mapping_username_help' => 'NameID will be used if attribute mapping is unspecified or invalid.',
|
||||
'saml_forcelogin_label' => 'SAML Force Login',
|
||||
'saml_forcelogin' => 'Make SAML the primary login',
|
||||
'saml_forcelogin_help' => 'You can use \'/login?nosaml\' to get to the normal login page.',
|
||||
'saml_slo_label' => 'SAML Single Log Out',
|
||||
'saml_slo' => 'Send a LogoutRequest to IdP on Logout',
|
||||
'saml_slo_help' => 'This will cause the user to be first redirected to the Idp on logout. Leave unchecked if the IdP doesn\'t correctly support SP-initiated SAML SLO.',
|
||||
'saml_custom_settings' => 'SAML Custom Settings',
|
||||
'saml_custom_settings_help' => 'You can specify additional settings to the onelogin/php-saml library. Use at your own risk.',
|
||||
'setting' => 'Setting',
|
||||
'settings' => 'Settings',
|
||||
'show_alerts_in_menu' => 'Show alerts in top menu',
|
||||
|
|
|
@ -4,6 +4,7 @@ return [
|
|||
'send_password_link' => 'Send Password Reset Link',
|
||||
'email_reset_password' => 'Email Password Reset',
|
||||
'reset_password' => 'Reset Password',
|
||||
'saml_login' => 'Login via SAML',
|
||||
'login' => 'Login',
|
||||
'login_prompt' => 'Please Login',
|
||||
'forgot_password' => 'I forgot my password',
|
||||
|
|
|
@ -61,6 +61,14 @@
|
|||
</div> <!-- end col-md-12 -->
|
||||
|
||||
</div> <!-- end row -->
|
||||
|
||||
@if ($snipeSettings->saml_enabled)
|
||||
<div class="row ">
|
||||
<div class="col-md-12 text-right">
|
||||
<a href="{{ route('saml.login') }}">{{ trans('auth/general.saml_login') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button class="btn btn-lg btn-primary btn-block">{{ trans('auth/general.login') }}</button>
|
||||
|
|
|
@ -222,6 +222,21 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-lg-3 col-sm-6 col-xl-1">
|
||||
<div class="box box-default">
|
||||
<div class="box-body text-center">
|
||||
<h5>
|
||||
<a href="{{ route('settings.saml.index') }}">
|
||||
<i class="fa fa-sign-in fa-4x" aria-hidden="true"></i>
|
||||
<br><br>
|
||||
<span class="name">SAML</span>
|
||||
</a>
|
||||
</h5>
|
||||
<p class="help-block">SAML settings</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-lg-3 col-sm-6 col-xl-1">
|
||||
<div class="box box-default">
|
||||
<div class="box-body text-center">
|
||||
|
|
177
resources/views/settings/saml.blade.php
Normal file
177
resources/views/settings/saml.blade.php
Normal file
|
@ -0,0 +1,177 @@
|
|||
@extends('layouts/default')
|
||||
|
||||
{{-- Page title --}}
|
||||
@section('title')
|
||||
Update SAML Settings
|
||||
@parent
|
||||
@stop
|
||||
|
||||
@section('header_right')
|
||||
<a href="{{ route('settings.index') }}" class="btn btn-default"> {{ trans('general.back') }}</a>
|
||||
@stop
|
||||
|
||||
|
||||
{{-- Page content --}}
|
||||
@section('content')
|
||||
|
||||
<style>
|
||||
.checkbox label {
|
||||
padding-right: 40px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
{{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'false', 'class' => 'form-horizontal', 'role' => 'form']) }}
|
||||
<!-- CSRF Token -->
|
||||
{{csrf_field()}}
|
||||
|
||||
<!-- this is a hack to prevent Chrome from trying to autocomplete fields -->
|
||||
<input type="text" name="prevent_autofill" id="prevent_autofill" value="" style="display:none;" />
|
||||
<input type="password" name="password_fake" id="password_fake" value="" style="display:none;" />
|
||||
|
||||
|
||||
@if (!empty($setting->saml_sp_x509cert))
|
||||
{{ Form::hidden('saml_sp_x509cert', $setting->saml_sp_x509cert) }}
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2">
|
||||
|
||||
|
||||
<div class="panel box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h2 class="box-title">
|
||||
<i class="fa fa-sign-in"></i> SAML
|
||||
</h4>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
|
||||
|
||||
<div class="col-md-11 col-md-offset-1">
|
||||
|
||||
<!-- Enable SAML -->
|
||||
<div class="form-group {{ $errors->has('saml_integration') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('saml_integration', trans('admin/settings/general.saml_integration')) }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{ Form::checkbox('saml_enabled', '1', Request::old('saml_enabled', $setting->saml_enabled), ['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
|
||||
{{ trans('admin/settings/general.saml_enabled') }}
|
||||
@if ($setting->saml_enabled)
|
||||
<p class="help-block"><a href="{{ route('saml.metadata') }}" target="_blank">{{ route('saml.metadata') }}</a></p>
|
||||
@endif
|
||||
{!! $errors->first('saml_enabled', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SAML IdP Metadata -->
|
||||
<div class="form-group {{ $errors->has('saml_idp_metadata') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('saml_idp_metadata', trans('admin/settings/general.saml_idp_metadata')) }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{ Form::textarea('saml_idp_metadata', old('saml_idp_metadata', $setting->saml_idp_metadata), ['class' => 'form-control','placeholder' => 'https://example.com/idp/metadata', 'wrap' => 'off', $setting->demoMode]) }}
|
||||
{!! $errors->first('saml_idp_metadata', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}<br>
|
||||
<button type="button" class="btn btn-default" id="saml_idp_metadata_upload_btn">{{ trans('button.select_file') }}</button>
|
||||
<input type="file" class="js-uploadFile" id="saml_idp_metadata_upload"
|
||||
data-maxsize="{{ \App\Helpers\Helper::file_upload_max_size() }}"
|
||||
accept="text/*" style="display:none; max-width: 90%">
|
||||
|
||||
<p class="help-block">{{ trans('admin/settings/general.saml_idp_metadata_help') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SAML Attribute Mapping Username -->
|
||||
<div class="form-group {{ $errors->has('saml_attr_mapping_username') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('saml_attr_mapping_username', trans('admin/settings/general.saml_attr_mapping_username')) }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{ Form::text('saml_attr_mapping_username', Request::old('saml_attr_mapping_username', $setting->saml_attr_mapping_username), ['class' => 'form-control','placeholder' => '', $setting->demoMode]) }}
|
||||
<p class="help-block">{{ trans('admin/settings/general.saml_attr_mapping_username_help') }}</p>
|
||||
{!! $errors->first('saml_attr_mapping_username', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
</div>
|
||||
</div><!-- AD Domain -->
|
||||
|
||||
<!-- SAML Force Login -->
|
||||
<div class="form-group">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('saml_forcelogin', trans('admin/settings/general.saml_forcelogin_label')) }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{ Form::checkbox('saml_forcelogin', '1', Request::old('saml_forcelogin', $setting->saml_forcelogin),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
|
||||
{{ trans('admin/settings/general.saml_forcelogin') }}
|
||||
<p class="help-block">{{ trans('admin/settings/general.saml_forcelogin_help') }}</p>
|
||||
<p class="help-block">{{ route('login', ['nosaml']) }}</p>
|
||||
{!! $errors->first('saml_forcelogin', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SAML Single Log Out -->
|
||||
<div class="form-group">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('saml_slo', trans('admin/settings/general.saml_slo_label')) }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{ Form::checkbox('saml_slo', '1', Request::old('saml_slo', $setting->saml_slo),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
|
||||
{{ trans('admin/settings/general.saml_slo') }}
|
||||
<p class="help-block">{{ trans('admin/settings/general.saml_slo_help') }}</p>
|
||||
{!! $errors->first('saml_slo', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SAML Custom Options -->
|
||||
<div class="form-group {{ $errors->has('saml_custom_settings') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('saml_custom_settings', trans('admin/settings/general.saml_custom_settings')) }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{ Form::textarea('saml_custom_settings', old('saml_custom_settings', $setting->saml_custom_settings), ['class' => 'form-control','placeholder' => 'example.option=false', 'wrap' => 'off', $setting->demoMode]) }}
|
||||
<p class="help-block">{{ trans('admin/settings/general.saml_custom_settings_help') }}</p>
|
||||
{!! $errors->first('saml_custom_settings', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div> <!--/.box-body-->
|
||||
<div class="box-footer">
|
||||
<div class="text-left col-md-6">
|
||||
<a class="btn btn-link text-left" href="{{ route('settings.index') }}">{{ trans('button.cancel') }}</a>
|
||||
</div>
|
||||
<div class="text-right col-md-6">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-check icon-white" aria-hidden="true"></i> {{ trans('general.save') }}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div> <!-- /box -->
|
||||
|
||||
</div> <!-- /.col-md-8-->
|
||||
</div> <!-- /.row-->
|
||||
|
||||
{{Form::close()}}
|
||||
|
||||
|
||||
@stop
|
||||
|
||||
@push('js')
|
||||
<script nonce="{{ csrf_token() }}">
|
||||
|
||||
$('#saml_idp_metadata_upload_btn').click(function() {
|
||||
$('#saml_idp_metadata_upload').click();
|
||||
});
|
||||
|
||||
$('#saml_idp_metadata_upload').on('change', function () {
|
||||
var fr = new FileReader();
|
||||
|
||||
fr.onload = function(e) {
|
||||
$('#saml_idp_metadata').text(e.target.result);
|
||||
}
|
||||
|
||||
fr.readAsText(this.files[0]);
|
||||
});
|
||||
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
|
Loading…
Reference in a new issue