Skip to content

Commit

Permalink
Fixes #106 - adds Google Authenticator support (#2842)
Browse files Browse the repository at this point in the history
* refactor to clean up LDAP login, and make the login method easier to handle.

* Login refactor cleanup

* Google 2FA package

* Adds Google Authenticator two-factor

* Removed unused blade

* Added optin setting in profile

* Removed dumb comments

* Made lock_passwords check more consistent

* Additional two factor strings

* Lock passwords check

* Display feature disabled text if in demo mode

* Two factor admin reset options

* Translation strings
  • Loading branch information
snipe committed Oct 29, 2016
1 parent 3a8edfd commit cea2559
Show file tree
Hide file tree
Showing 110 changed files with 1,403 additions and 307 deletions.
199 changes: 124 additions & 75 deletions app/Http/Controllers/Auth/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Redirect;
use Log;
use View;
use PragmaRX\Google2FA\Google2FA;



Expand Down Expand Up @@ -48,7 +49,7 @@ class AuthController extends Controller
*/
public function __construct()
{
$this->middleware('guest', ['except' => 'logout']);
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
}


Expand All @@ -63,6 +64,51 @@ function showLoginForm()
return View::make('auth.login');
}

private function login_via_ldap(Request $request)
{
LOG::debug("Binding user to LDAP.");
$ldap_user = Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'));
if(!$ldap_user) {
LOG::debug("LDAP user ".$request->input('username')." not found in LDAP or could not bind");
throw new \Exception("Could not find user in LDAP directory");
} else {
LOG::debug("LDAP user ".$request->input('username')." successfully bound to LDAP");
}

// Check if the user exists in the database
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->first();
LOG::debug("Local auth lookup complete");

// The user does not exist in the database. Try to get them from LDAP.
// If user does not exist and authenticates successfully with LDAP we
// will create it on the fly and sign in with default permissions
if (!$user) {
LOG::debug("Local user ".Input::get('username')." does not exist");
LOG::debug("Creating local user ".Input::get('username'));

if ($user = Ldap::createUserFromLdap($ldap_user)) { //this handles passwords on its own
LOG::debug("Local user created.");
} else {
LOG::debug("Could not create local user.");
throw new \Exception("Could not create local user");
}
// If the user exists and they were imported from LDAP already
} else {
LOG::debug("Local user ".$request->input('username')." exists in database. Updating existing user against LDAP.");

$ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user);

if (Setting::getSettings()->ldap_pw_sync=='1') {
$user->password = bcrypt($request->input('password'));
}

$user->email = $ldap_attr['email'];
$user->first_name = $ldap_attr['firstname'];
$user->last_name = $ldap_attr['lastname'];
$user->save();
} // End if(!user)
return $user;
}


/**
Expand All @@ -77,120 +123,123 @@ public function login(Request $request)
if ($validator->fails()) {
return redirect()->back()->withInput()->withErrors($validator);
}

$user = null;
// Should we even check for LDAP users?
if (Setting::getSettings()->ldap_enabled=='1') {

LOG::debug("LDAP is enabled.");
// Check if the user exists in the database
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->first();
LOG::debug("Local auth lookup complete");

try {
Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'));
LOG::debug("Binding user to LDAP.");
$user = $this->login_via_ldap($request);
Auth::login($user, true);
} catch (\Exception $e) {
LOG::debug("User ".Input::get('username').' did not authenticate successfully against LDAP.');
//$ldap_error = $e->getMessage();
// return redirect()->back()->withInput()->with('error',$e->getMessage());
if(Setting::getSettings()->ldap_pw_sync!='1') {
return redirect()->back()->withInput()->with('error',$e->getMessage());
}
}
}

// If the user wasn't authenticated via LDAP, skip to local auth
if(!$user) {
LOG::debug("Authenticating user against database.");
// Try to log the user in
if (!Auth::attempt(Input::only('username', 'password'), Input::get('remember-me', 0))) {
LOG::debug("Local authentication failed.");
return redirect()->back()->withInput()->with('error', trans('auth/message.account_not_found'));
}
}

// The user does not exist in the database. Try to get them from LDAP.
// If user does not exist and authenticates sucessfully with LDAP we
// will create it on the fly and sign in with default permissions
if (!$user) {
LOG::debug("Local user ".Input::get('username')." does not exist");
// Get the page we were before
$redirect = \Session::get('loginRedirect', 'home');

try {
// Unset the page we were before from the session
\Session::forget('loginRedirect');

if ($userattr = Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'))) {
LOG::debug("Creating local user ".Input::get('username'));

if ($newuser = Ldap::createUserFromLdap($userattr)) {
LOG::debug("Local user created.");
} else {
LOG::debug("Could not create local user.");
}

} else {
LOG::debug("User did not authenticate correctly against LDAP. No local user was created.");
}
// Redirect to the users page
return redirect()->to($redirect)->with('success', trans('auth/message.signin.success'));
}

} catch (\Exception $e) {
return redirect()->back()->withInput()->with('error',$e->getMessage());
}

// If the user exists and they were imported from LDAP already
} else {
/**
* Two factor enrollment page
*
* @return Redirect
*/
public function getTwoFactorEnroll()
{

LOG::debug("Local user ".Input::get('username')." exists in database. Authenticating existing user against LDAP.");
if (!Auth::check()) {
return redirect()->route('login')->with('error', 'You must be logged in.');
}

if ($ldap_user = Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'))) {
$ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user);
$user = Auth::user();
$google2fa = app()->make('PragmaRX\Google2FA\Contracts\Google2FA');

LOG::debug("Valid LDAP login. Updating the local data.");
if ($user->two_factor_secret=='') {
$user->two_factor_secret = $google2fa->generateSecretKey();
$user->save();
}

if (Setting::getSettings()->ldap_pw_sync=='1') {
$user->password = bcrypt($request->input('password'));
}

$user->email = $ldap_attr['email'];
$user->first_name = $ldap_attr['firstname'];
$user->last_name = $ldap_attr['lastname'];
$user->save();
$google2fa_url = $google2fa->getQRCodeGoogleUrl(
Setting::getSettings()->site_name,
$user->username,
$user->two_factor_secret
);

if (Setting::getSettings()->ldap_pw_sync!='1') {
Auth::login($user, true);
// Redirect to the users page
return redirect()->to('/home')->with('success', trans('auth/message.signin.success'));
}
return View::make('auth.two_factor_enroll')->with('google2fa_url',$google2fa_url);

} else {
LOG::debug("User ".Input::get('username')." did not authenticate correctly against LDAP. Local user was not updated.");
}// End LDAP auth
}

} // End if(!user)

// NO LDAP enabled - just try to login the user normally
}
/**
* Two factor code form page
*
* @return Redirect
*/
public function getTwoFactorAuth() {
return View::make('auth.two_factor');
}

/**
* Two factor code submission
*
* @return Redirect
*/
public function postTwoFactorAuth(Request $request) {

LOG::debug("Authenticating user against database.");
// Try to log the user in
if (!Auth::attempt(Input::only('username', 'password'), Input::get('remember-me', 0))) {
LOG::debug("Local authentication failed.");
// throw new Cartalyst\Sentry\Users\UserNotFoundException();
return redirect()->back()->withInput()->with('error', trans('auth/message.account_not_found'));
if (!Auth::check()) {
return redirect()->route('login')->with('error', 'You must be logged in.');
}

$user = Auth::user();
$secret = $request->get('two_factor_secret');
$google2fa = app()->make('PragmaRX\Google2FA\Contracts\Google2FA');
$valid = $google2fa->verifyKey($user->two_factor_secret, $secret);

if ($valid) {
$user->two_factor_enrolled = 1;
$user->save();
$request->session()->put('2fa_authed', 'true');
return redirect()->route('home')->with('success', 'You are logged in!');
}

// Get the page we were before
$redirect = \Session::get('loginRedirect', 'home');

// Unset the page we were before from the session
\Session::forget('loginRedirect');
return redirect()->route('two-factor')->with('error', 'Invalid two-factor code');

// Redirect to the users page
return redirect()->to($redirect)->with('success', trans('auth/message.signin.success'));

// Ooops.. something went wrong
return redirect()->back()->withInput()->withErrors($this->messageBag);
}


/**
* Logout page.
*
* @return Redirect
*/
public function logout()
public function logout(Request $request)
{
// Log the user out
$request->session()->forget('2fa_authed');
Auth::logout();

// Redirect to the users page
return redirect()->route('home')->with('success', 'You have successfully logged out!');
return redirect()->route('login')->with('success', 'You have successfully logged out!');
}


Expand Down
5 changes: 5 additions & 0 deletions app/Http/Controllers/ProfileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use View;
use Auth;
use App\Helpers\Helper;
use App\Models\Setting;

/**
* This controller handles all actions related to User Profiles for
Expand Down Expand Up @@ -53,6 +54,10 @@ public function postIndex()
$user->gravatar = e(Input::get('gravatar'));
$user->locale = e(Input::get('locale'));

if ((Setting::getSettings()->two_factor_enabled=='1') && (!config('app.lock_passwords'))) {
$user->two_factor_optin = e(Input::get('two_factor_optin', '0'));
}

if (Input::file('avatar')) {
$image = Input::file('avatar');
$file_name = str_slug($user->first_name."-".$user->last_name).".".$image->getClientOriginalExtension();
Expand Down
8 changes: 3 additions & 5 deletions app/Http/Controllers/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,7 @@ public function getSetupMigrate()
*/
public function getIndex()
{
// Grab all the settings
$settings = Setting::all();

// Show the page
return View::make('settings/index', compact('settings'));
}

Expand Down Expand Up @@ -316,10 +313,11 @@ public function postEdit()
}


if (config('app.lock_passwords')==false) {
if (!config('app.lock_passwords')) {
$setting->site_name = e(Input::get('site_name'));
$setting->brand = e(Input::get('brand'));
$setting->custom_css = e(Input::get('custom_css'));
$setting->two_factor_enabled = e(Input::get('two_factor_enabled'));
}

if (Input::get('per_page')!='') {
Expand Down Expand Up @@ -379,7 +377,7 @@ public function postEdit()
}

$alert_email = rtrim(Input::get('alert_email'), ',');
$alert_email = trim(Input::get('alert_email'));
$alert_email = trim($alert_email);

$setting->alert_email = e($alert_email);
$setting->alerts_enabled = e(Input::get('alerts_enabled', '0'));
Expand Down
19 changes: 19 additions & 0 deletions app/Http/Controllers/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1365,4 +1365,23 @@ public function getExportUserCsv()
return $response;

}


public function postTwoFactorReset(Request $request)
{
if (Gate::denies('users.edit')) {
return response()->json(['message' => trans('general.insufficient_permissions')], 500);
}

try {
$user = User::find($request->get('id'));
$user->two_factor_secret = null;
$user->two_factor_enrolled = 0;
$user->save();
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_success')], 200);
} catch (\Exception $e) {
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_error')], 500);
}

}
}
1 change: 1 addition & 0 deletions app/Http/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Kernel extends HttpKernel
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\App\Http\Middleware\CheckLocale::class,
\App\Http\Middleware\CheckForTwoFactor::class,
],

'api' => [
Expand Down
Loading

0 comments on commit cea2559

Please sign in to comment.