Fix API auth issues (#9185)

* Fix API auth issues
Api access page now creates tokens with the correct ID.
Correctly creates users for legacy user tokens.
Fix Ldap comparison
Laravel Util class to make code easier to access/read

* More api access page fixes

* fix style
This commit is contained in:
Tony Murray 2018-09-11 22:36:52 -05:00 committed by GitHub
parent c3a24fe88f
commit e8cf6bb385
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 168 additions and 90 deletions

View File

@ -194,7 +194,7 @@ class LdapAuthorizer extends AuthorizerBase
public function getUser($user_id)
{
foreach ($this->getUserlist() as $user) {
if ($user['user_id'] === $user_id) {
if ((int)$user['user_id'] === (int)$user_id) {
return $user;
}
}

116
LibreNMS/Util/Laravel.php Normal file
View File

@ -0,0 +1,116 @@
<?php
/**
* Laravel.php
*
* Utility class to gather code to do
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2018 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace LibreNMS\Util;
use App;
use Illuminate\Database\Events\QueryExecuted;
use LibreNMS\Config;
use LibreNMS\DB\Eloquent;
use Log;
class Laravel
{
public static function enableQueryDebug()
{
$db = Eloquent::DB();
if ($db && !$db->getEventDispatcher()->hasListeners('Illuminate\Database\Events\QueryExecuted')) {
$db->listen(function (QueryExecuted $query) {
// collect bindings and make them a little more readable
$bindings = collect($query->bindings)->map(function ($item) {
if ($item instanceof \Carbon\Carbon) {
return $item->toDateTimeString();
}
return $item;
})->toJson();
if (class_exists('Log')) {
Log::debug("SQL[%Y{$query->sql} %y$bindings%n {$query->time}ms] \n", ['color' => true]);
} else {
c_echo("SQL[%Y{$query->sql} %y$bindings%n {$query->time}ms] \n");
}
});
}
}
public static function disableQueryDebug()
{
$db = Eloquent::DB();
if ($db) {
// remove all query executed event handlers
$db->getEventDispatcher()->flush('Illuminate\Database\Events\QueryExecuted');
}
}
public static function enableCliDebugOutput()
{
if (class_exists('Log')) {
$logger = Log::getMonolog();
// only install if not existing
$install = true;
$logfile = Config::get('log_file', base_path('logs/librenms.log'));
foreach ($logger->getHandlers() as $handler) {
if ($handler instanceof \Monolog\Handler\StreamHandler) {
if ($handler->getUrl() == 'php://stdout') {
$install = false;
} elseif ($handler->getUrl() == $logfile) {
// send to librenms log file if not a cli app
if (!App::runningInConsole()) {
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
Log::error("$errno $errfile:$errline $errstr");
});
$handler->setLevel(\Monolog\Logger::DEBUG);
}
}
}
}
if ($install) {
$handler = new \Monolog\Handler\StreamHandler(
'php://stdout',
\Monolog\Logger::DEBUG
);
$handler->setFormatter(new CliColorFormatter());
$logger->pushHandler($handler);
}
}
}
public static function disableCliDebugOutput()
{
if (class_exists('Log')) {
$handlers = Log::getMonolog()->getHandlers();
if (isset($handlers[0]) && $handlers[0]->getUrl() == 'php://stdout') {
Log::getMonolog()->popHandler();
}
}
}
}

View File

@ -34,6 +34,7 @@ use LibreNMS\Authentication\LegacyAuth;
use LibreNMS\Exceptions\AuthenticationException;
use Request;
use Session;
use Toastr;
class LegacyUserProvider implements UserProvider
{
@ -50,6 +51,21 @@ class LegacyUserProvider implements UserProvider
return $this->fetchUserByName($username);
}
/**
* Retrieve a user by their legacy auth specific identifier.
*
* @param int $identifier
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByLegacyId($identifier)
{
error_reporting(0);
$legacy_user = LegacyAuth::get()->getUser($identifier);
error_reporting(-1);
return $this->fetchUserByName($legacy_user['username']);
}
/**
* Retrieve a user by their unique identifier and "remember me" token.
*
@ -181,10 +197,11 @@ class LegacyUserProvider implements UserProvider
error_reporting(-1);
} catch (AuthenticationException $ae) {
//
Toastr::error($ae->getMessage());
}
if (empty($new_user)) {
Toastr::info("No user ($auth_id) [$username]");
return null;
}
}

View File

@ -72,10 +72,17 @@ class TokenUserProvider extends LegacyUserProvider implements UserProvider
return $user;
}
// missing user for existing token, create it
$user_id = ApiToken::idFromToken($credentials['api_token']);
// missing user for existing token, create it assuming legacy auth_id
$api_token = ApiToken::where('token_hash', $credentials['api_token'])->first();
$user = $this->retrieveByLegacyId($api_token->user_id);
return $this->retrieveById($user_id);
// update token user_id
if ($user) {
$api_token->user_id = $user->user_id;
$api_token->save();
}
return $user;
}
/**

View File

@ -12,6 +12,8 @@
* the source code distribution for details.
*/
use App\Models\ApiToken;
use App\Models\User;
use LibreNMS\Authentication\LegacyAuth;
if (LegacyAuth::user()->hasGlobalAdmin()) {
@ -56,8 +58,8 @@ if (LegacyAuth::user()->hasGlobalAdmin()) {
<div class="col-sm-4">
<select class="form-control" id="user_id" name="user_id">
<?php
foreach ($userlist = LegacyAuth::get()->getUserlist() as $users) {
echo '<option value="'.$users['user_id'].'">'.$users['username'].'</option>';
foreach ($userlist = User::all() as $user) {
echo '<option value="' . $user->user_id . '">' . $user->username . ' (' . $user->auth_type . ')</option>';
}
?>
@ -130,6 +132,7 @@ echo '
<table class="table table-bordered table-condensed">
<tr>
<th>User</th>
<th>Auth</th>
<th>Token Hash</th>
<th>QR Code</th>
<th>Description</th>
@ -138,25 +141,21 @@ echo '
</tr>
';
foreach (dbFetchRows('SELECT * FROM `api_tokens` ORDER BY user_id') as $api) {
if ($api['disabled'] == '1') {
$api_disabled = 'checked';
} else {
$api_disabled = '';
}
foreach ($userlist as $tmp_user) {
if ($tmp_user['user_id'] === $api['user_id']) {
$user_details = $tmp_user;
}
}
foreach (ApiToken::all() as $api) {
$user_details = $userlist->where('user_id', $api->user_id)->first();
$api_disabled = $api->disabled == 1 ? 'checked' : '';
$color = $user_details->auth_type == LegacyAuth::getType() ? '' : 'bgcolor="lightgrey"';
echo '
<tr id="'.$api['id'].'">
<td>'.$user_details['username'].'</td>
<td>'.$api['token_hash'].'</td>
<td><button class="btn btn-info btn-xs" data-toggle="modal" data-target="#display-qr" data-token_hash="'.$api['token_hash'].'"><i class="fa fa-qrcode" ></i></button></td>
<td>'.$api['description'].'</td>
<td><input type="checkbox" name="token-status" data-token_id="'.$api['id'].'" data-off-text="No" data-on-text="Yes" data-on-color="danger" '.$api_disabled.' data-size="mini"></td>
<td><button type="button" class="btn btn-danger btn-xs" id="'.$api['id'].'" data-token_id="'.$api['id'].'" data-toggle="modal" data-target="#confirm-delete">Delete</button></td>
<tr id="'.$api->id.'" ' . $color . '>
<td>'.$user_details->username.'</td>
<td>'.$user_details->auth_type.'</td>
<td>'.$api->token_hash.'</td>
<td><button class="btn btn-info btn-xs" data-toggle="modal" data-target="#display-qr" data-token_hash="'.$api->token_hash.'"><i class="fa fa-qrcode" ></i></button></td>
<td>'.$api->description.'</td>
<td><input type="checkbox" name="token-status" data-token_id="'.$api->id.'" data-off-text="No" data-on-text="Yes" data-on-color="danger" '.$api_disabled.' data-size="mini"></td>
<td><button type="button" class="btn btn-danger btn-xs" id="'.$api->id.'" data-token_id="'.$api->id.'" data-toggle="modal" data-target="#confirm-delete">Delete</button></td>
</tr>
';
}

View File

@ -37,8 +37,6 @@ function set_debug($state = true, $silence = false)
$debug = $state; // set to global
$db = \LibreNMS\DB\Eloquent::DB();
restore_error_handler(); // disable Laravel error handler
if (isset($debug) && $debug) {
@ -47,75 +45,16 @@ function set_debug($state = true, $silence = false)
ini_set('log_errors', 0);
error_reporting(E_ALL & ~E_NOTICE);
if (class_exists('Log')) {
$logger = Log::getMonolog();
// only install if not existing
$install = true;
$logfile = Config::get('log_file', base_path('logs/librenms.log'));
foreach ($logger->getHandlers() as $handler) {
if ($handler instanceof \Monolog\Handler\StreamHandler) {
if ($handler->getUrl() == 'php://stdout') {
$install = false;
} elseif ($handler->getUrl() == $logfile) {
// send to librenms log file if not a cli app
if (!App::runningInConsole()) {
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
Log::error("$errno $errfile:$errline $errstr");
});
$handler->setLevel(\Monolog\Logger::DEBUG);
}
}
}
}
if ($install) {
$handler = new \Monolog\Handler\StreamHandler(
'php://stdout',
\Monolog\Logger::DEBUG
);
$handler->setFormatter(new LibreNMS\Util\CliColorFormatter());
$logger->pushHandler($handler);
}
}
if ($db && !$db->getEventDispatcher()->hasListeners('Illuminate\Database\Events\QueryExecuted')) {
$db->listen(function (QueryExecuted $query) {
// collect bindings and make them a little more readable
$bindings = collect($query->bindings)->map(function ($item) {
if ($item instanceof \Carbon\Carbon) {
return $item->toDateTimeString();
}
return $item;
})->toJson();
if (class_exists('Log')) {
Log::debug("SQL[%Y{$query->sql} %y$bindings%n {$query->time}ms] \n", ['color' => true]);
} else {
c_echo("SQL[%Y{$query->sql} %y$bindings%n {$query->time}ms] \n");
}
});
}
\LibreNMS\Util\Laravel::enableCliDebugOutput();
\LibreNMS\Util\Laravel::enableQueryDebug();
} else {
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
ini_set('log_errors', 1);
error_reporting($silence ? 0 : E_ERROR);
if (class_exists('Log')) {
$handlers = Log::getMonolog()->getHandlers();
if (isset($handlers[0]) && $handlers[0]->getUrl() == 'php://stdout') {
Log::getMonolog()->popHandler();
}
}
if ($db) {
// remove all query executed event handlers
$db->getEventDispatcher()->flush('Illuminate\Database\Events\QueryExecuted');
}
\LibreNMS\Util\Laravel::enableCliDebugOutput();
\LibreNMS\Util\Laravel::disableQueryDebug();
}
return $debug;