snipe-it/app/Http/Controllers/Auth/LoginController.php

473 lines
14 KiB
PHP
Raw Normal View History

2016-03-25 01:18:05 -07:00
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
2016-03-25 19:26:22 -07:00
use App\Models\Setting;
use App\Models\User;
use App\Services\LdapAd;
2020-05-05 07:06:19 -07:00
use App\Services\Saml;
2019-03-27 22:01:38 -07:00
use Com\Tecnick\Barcode\Barcode;
2019-03-26 14:10:56 -07:00
use Google2FA;
use Illuminate\Foundation\Auth\ThrottlesLogins;
2016-03-25 01:18:05 -07:00
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
2016-03-25 01:18:05 -07:00
use Log;
2019-03-26 14:10:56 -07:00
use Redirect;
2016-03-25 01:18:05 -07:00
2016-04-07 13:21:09 -07:00
/**
* This controller handles authentication for the user, including local
* database users and LDAP users.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @version v1.0
*/
class LoginController extends Controller
2016-03-25 01:18:05 -07:00
{
use ThrottlesLogins;
2016-03-25 01:18:05 -07:00
// This tells the auth controller to use username instead of email address
protected $username = 'username';
/**
* Where to redirect users after login / registration.
*
* @var string
*/
protected $redirectTo = '/';
/**
* @var LdapAd
*/
protected $ldap;
2020-05-05 07:06:19 -07:00
/**
* @var Saml
*/
protected $saml;
2016-03-25 01:18:05 -07:00
/**
* Create a new authentication controller instance.
*
* @param LdapAd $ldap
2020-05-05 07:06:19 -07:00
* @param Saml $saml
*
2016-03-25 01:18:05 -07:00
* @return void
*/
2020-05-05 07:06:19 -07:00
public function __construct(LdapAd $ldap, Saml $saml)
2016-03-25 01:18:05 -07:00
{
parent::__construct();
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
Session::put('backUrl', \URL::previous());
$this->ldap = $ldap;
2020-05-05 07:06:19 -07:00
$this->saml = $saml;
2016-03-25 01:18:05 -07:00
}
function showLoginForm(Request $request)
2016-03-25 01:18:05 -07:00
{
$this->loginViaRemoteUser($request);
2020-05-05 07:06:19 -07:00
$this->loginViaSaml($request);
2016-03-25 01:18:05 -07:00
if (Auth::check()) {
2020-05-05 07:06:19 -07:00
return redirect()->intended('/');
}
if ($this->saml->isEnabled() && Setting::getSettings()->saml_forcelogin == "1" && !($request->has('nosaml') || $request->session()->has('error'))) {
return redirect()->route('saml.login');
2016-03-25 01:18:05 -07:00
}
if (Setting::getSettings()->login_common_disabled == "1") {
return view('errors.403');
}
return view('auth.login');
2016-03-25 01:18:05 -07:00
}
2020-05-05 07:06:19 -07:00
/**
* 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
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param Request $request
*
* @return User
*
* @throws \Exception
*/
private function loginViaLdap(Request $request): User
{
try {
return $this->ldap->ldapLogin($request->input('username'), $request->input('password'));
} catch (\Exception $ex) {
LOG::debug("LDAP user login: " . $ex->getMessage());
throw new \Exception($ex->getMessage());
}
}
private function loginViaRemoteUser(Request $request)
{
$header_name = Setting::getSettings()->login_remote_user_header_name ?: 'REMOTE_USER';
$remote_user = $request->server($header_name);
if (Setting::getSettings()->login_remote_user_enabled == "1" && isset($remote_user) && !empty($remote_user)) {
Log::debug("Authenticating via HTTP header $header_name.");
$pos = strpos($remote_user, '\\');
if ($pos > 0) {
$remote_user = substr($remote_user, $pos + 1);
};
try {
2018-07-19 10:22:08 -07:00
$user = User::where('username', '=', $remote_user)->whereNull('deleted_at')->where('activated', '=', '1')->first();
2018-09-26 19:06:31 -07:00
Log::debug("Remote user auth lookup complete");
if(!is_null($user)) Auth::login($user, true);
} catch(Exception $e) {
Log::debug("There was an error authenticating the Remote user: " . $e->getMessage());
}
}
}
2016-12-01 02:25:53 -08:00
2016-03-25 01:18:05 -07:00
/**
* Account sign in form processing.
*
* @return Redirect
*/
public function login(Request $request)
2016-03-25 01:18:05 -07:00
{
if (Setting::getSettings()->login_common_disabled == "1") {
return view('errors.403');
}
$validator = $this->validator($request->all());
2016-03-25 01:18:05 -07:00
if ($validator->fails()) {
2016-04-28 21:06:41 -07:00
return redirect()->back()->withInput()->withErrors($validator);
2016-03-25 01:18:05 -07:00
}
$this->maxLoginAttempts = config('auth.throttle.max_attempts');
$this->lockoutTime = config('auth.throttle.lockout_duration');
if ($lockedOut = $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$user = null;
2016-03-25 01:18:05 -07:00
// Should we even check for LDAP users?
if ($this->ldap->init()) {
2016-03-25 01:18:05 -07:00
LOG::debug("LDAP is enabled.");
try {
LOG::debug("Attempting to log user in by LDAP authentication.");
$user = $this->loginViaLdap($request);
2016-12-29 14:02:18 -08:00
Auth::login($user, true);
// If the user was unable to login via LDAP, log the error and let them fall through to
// local authentication.
} catch (\Exception $e) {
Log::debug("There was an error authenticating the LDAP user: ".$e->getMessage());
}
}
2016-03-25 01:18:05 -07:00
// If the user wasn't authenticated via LDAP, skip to local auth
2016-12-29 14:02:18 -08:00
if (!$user) {
2018-09-26 19:06:31 -07:00
Log::debug("Authenticating user against database.");
// Try to log the user in
2018-07-16 23:48:46 -07:00
if (!Auth::attempt(['username' => $request->input('username'), 'password' => $request->input('password'), 'activated' => 1], $request->input('remember'))) {
2016-12-29 14:02:18 -08:00
if (!$lockedOut) {
$this->incrementLoginAttempts($request);
}
2018-09-26 19:06:31 -07:00
Log::debug("Local authentication failed.");
2016-12-29 14:02:18 -08:00
return redirect()->back()->withInput()->with('error', trans('auth/message.account_not_found'));
} else {
2017-03-03 18:28:13 -08:00
$this->clearLoginAttempts($request);
2016-12-29 14:02:18 -08:00
}
}
2017-03-03 18:28:13 -08:00
if ($user = Auth::user()) {
$user->last_login = \Carbon::now();
$user->save();
}
// Redirect to the users page
return redirect()->intended()->with('success', trans('auth/message.signin.success'));
}
2016-03-25 01:18:05 -07:00
/**
* Two factor enrollment page
*
* @return Redirect
*/
public function getTwoFactorEnroll()
{
2016-03-25 01:18:05 -07:00
// Make sure the user is logged in
if (!Auth::check()) {
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
}
2016-03-25 01:18:05 -07:00
$settings = Setting::getSettings();
$user = Auth::user();
2016-07-14 23:49:32 -07:00
// We wouldn't normally see this page if 2FA isn't enforced via the
// \App\Http\Middleware\CheckForTwoFactor middleware AND if a device isn't enrolled,
// but let's check check anyway in case there's a browser history or back button thing.
// While you can access this page directly, enrolling a device when 2FA isn't enforced
// won't cause any harm.
2016-07-14 23:49:32 -07:00
if (($user->two_factor_secret!='') && ($user->two_factor_enrolled==1)) {
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.already_enrolled'));
}
2019-03-27 22:01:38 -07:00
$secret = Google2FA::generateSecretKey();
$user->two_factor_secret = $secret;
$user->save();
2016-03-25 01:18:05 -07:00
2019-03-27 22:01:38 -07:00
$barcode = new Barcode();
$barcode_obj =
$barcode->getBarcodeObj(
'QRCODE',
sprintf(
'otpauth://totp/%s:%s?secret=%s&issuer=Snipe-IT&period=30',
urlencode($settings->site_name),
urlencode($user->username),
urlencode($secret)
),
300,
300,
'black',
[-2, -2, -2, -2]
);
2019-03-27 22:01:38 -07:00
return view('auth.two_factor_enroll')->with('barcode_obj', $barcode_obj);
}
2016-03-25 01:18:05 -07:00
/**
* Two factor code form page
*
* @return Redirect
*/
2016-12-29 14:02:18 -08:00
public function getTwoFactorAuth()
{
// Check that the user is logged in
if (!Auth::check()) {
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
}
$user = Auth::user();
// Check whether there is a device enrolled.
// This *should* be handled via the \App\Http\Middleware\CheckForTwoFactor middleware
// but we're just making sure (in case someone edited the database directly, etc)
if (($user->two_factor_secret=='') || ($user->two_factor_enrolled!=1)) {
return redirect()->route('two-factor-enroll');
}
return view('auth.two_factor');
}
2016-03-25 01:18:05 -07:00
/**
* Two factor code submission
*
* @param Request $request
*
* @return Redirect
*/
2016-12-29 14:02:18 -08:00
public function postTwoFactorAuth(Request $request)
{
if (!Auth::check()) {
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
}
2019-05-23 17:39:50 -07:00
if (!$request->filled('two_factor_secret')) {
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.code_required'));
2016-03-25 01:18:05 -07:00
}
if (!$request->has('two_factor_secret')) {
return redirect()->route('two-factor')->with('error', 'Two-factor code is required.');
}
$user = Auth::user();
$secret = $request->input('two_factor_secret');
2019-03-27 22:01:38 -07:00
if (Google2FA::verifyKey($user->two_factor_secret, $secret)) {
$user->two_factor_enrolled = 1;
$user->save();
$request->session()->put('2fa_authed', 'true');
return redirect()->route('home')->with('success', 'You are logged in!');
}
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.invalid_code'));
2016-03-25 01:18:05 -07:00
}
2016-03-25 01:18:05 -07:00
/**
* Logout page.
*
* @param Request $request
*
2016-03-25 01:18:05 -07:00
* @return Redirect
*/
public function logout(Request $request)
2016-03-25 01:18:05 -07:00
{
2020-05-05 07:06:19 -07:00
$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()->regenerate(true);
2016-03-25 01:18:05 -07:00
Auth::logout();
2020-05-05 07:06:19 -07:00
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);
}
2020-05-05 07:06:19 -07:00
return redirect()->route('login')->with(['success' => trans('auth/message.logout.success'), 'loggedout' => true]);
2016-03-25 01:18:05 -07:00
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'username' => 'required',
'password' => 'required',
]);
}
public function username()
{
return 'username';
}
/**
* Redirect the user after determining they are locked out.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendLockoutResponse(Request $request)
{
2016-12-14 06:30:51 -08:00
$seconds = $this->limiter()->availableIn(
$this->throttleKey($request)
);
$minutes = round($seconds / 60);
2016-12-29 14:02:18 -08:00
$message = \Lang::get('auth/message.throttle', ['minutes' => $minutes]);
2016-12-14 06:30:51 -08:00
return redirect()->back()
2016-12-14 06:30:51 -08:00
->withInput($request->only($this->username(), 'remember'))
->withErrors([$this->username() => $message]);
}
2016-12-14 06:30:51 -08:00
/**
* Override the lockout time and duration
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
2016-12-14 06:30:51 -08:00
protected function hasTooManyLoginAttempts(Request $request)
{
$lockoutTime = config('auth.throttle.lockout_duration');
$maxLoginAttempts = config('auth.throttle.max_attempts');
return $this->limiter()->tooManyAttempts(
2016-12-29 14:02:18 -08:00
$this->throttleKey($request),
$maxLoginAttempts,
$lockoutTime
2016-12-14 06:30:51 -08:00
);
}
2017-02-02 18:14:25 -08:00
public function legacyAuthRedirect() {
return redirect()->route('login');
}
2017-10-02 16:00:42 -07:00
public function redirectTo()
{
return Session::get('backUrl') ? Session::get('backUrl') : $this->redirectTo;
}
2016-03-25 01:18:05 -07:00
}