Implement OAuth and SAML2 support (#13764)
* Implement OAuth and SAML2 support via Socialite * Add socialite docs * fixes * Additional information added * wip * 22.3.0 targeted version * Allow mysql auth as long as there is a password saved Co-authored-by: laf <[email protected]> Co-authored-by: Tony Murray <[email protected]>
@@ -18,7 +18,7 @@ class MysqlAuthorizer extends AuthorizerBase
|
||||
$username = $credentials['username'] ?? null;
|
||||
$password = $credentials['password'] ?? null;
|
||||
|
||||
$user_data = User::thisAuth()->firstWhere(['username' => $username]);
|
||||
$user_data = User::whereNotNull('password')->firstWhere(['username' => $username]);
|
||||
$hash = $user_data->password;
|
||||
$enabled = $user_data->enabled;
|
||||
|
||||
@@ -76,7 +76,7 @@ class MysqlAuthorizer extends AuthorizerBase
|
||||
$user_id = $new_user->user_id;
|
||||
|
||||
// set auth_id
|
||||
$new_user->auth_id = $this->getUserid($username);
|
||||
$new_user->auth_id = (string) $this->getUserid($username);
|
||||
$new_user->save();
|
||||
|
||||
if ($user_id) {
|
||||
|
||||
@@ -83,6 +83,18 @@ class DynamicConfigItem implements \ArrayAccess
|
||||
return filter_var($value, FILTER_VALIDATE_EMAIL);
|
||||
} elseif ($this->type == 'array') {
|
||||
return is_array($value); // this should probably have more complex validation via validator rules
|
||||
} elseif ($this->type == 'array-sub-keyed') {
|
||||
if (! is_array($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($value as $v) {
|
||||
if (! is_array($v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} elseif ($this->type == 'color') {
|
||||
return (bool) preg_match('/^#?[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/', $value);
|
||||
} elseif (in_array($this->type, ['text', 'password'])) {
|
||||
|
||||
@@ -97,7 +97,7 @@ class AddUserCommand extends LnmsCommand
|
||||
$user->setPassword($password);
|
||||
$user->save();
|
||||
|
||||
$user->auth_id = LegacyAuth::get()->getUserid($user->username) ?: $user->user_id;
|
||||
$user->auth_id = (string) LegacyAuth::get()->getUserid($user->username) ?: $user->user_id;
|
||||
$user->save();
|
||||
|
||||
$this->info(__('commands.user:add.success', ['username' => $user->username]));
|
||||
|
||||
@@ -41,13 +41,21 @@ class LoginController extends Controller
|
||||
$this->middleware('guest')->except('logout');
|
||||
}
|
||||
|
||||
public function username()
|
||||
public function username(): string
|
||||
{
|
||||
return 'username';
|
||||
}
|
||||
|
||||
public function showLoginForm()
|
||||
/**
|
||||
* @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse|\Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function showLoginForm(Request $request)
|
||||
{
|
||||
// Check if we want to redirect users to the socialite provider directly
|
||||
if (! $request->has('redirect') && Config::get('auth.socialite.redirect') && array_key_first(Config::get('auth.socialite.configs', []))) {
|
||||
return (new SocialiteController)->redirect($request, array_key_first(Config::get('auth.socialite.configs', [])));
|
||||
}
|
||||
|
||||
if (Config::get('public_status')) {
|
||||
$devices = Device::isActive()->with('location')->get();
|
||||
|
||||
@@ -57,7 +65,7 @@ class LoginController extends Controller
|
||||
return view('auth.login');
|
||||
}
|
||||
|
||||
protected function loggedOut(Request $request)
|
||||
protected function loggedOut(Request $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
return redirect(Config::get('auth_logout_handler', $this->redirectTo));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
/**
|
||||
* SocialiateController.php
|
||||
*
|
||||
* -Description-
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Config;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Laravel\Socialite\Contracts\User as SocialiteUser;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use LibreNMS\Config as LibreNMSConfig;
|
||||
use LibreNMS\Exceptions\AuthenticationException;
|
||||
use Log;
|
||||
|
||||
class SocialiteController extends Controller
|
||||
{
|
||||
/** @var SocialiteUser */
|
||||
private $socialite_user;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->injectConfig();
|
||||
}
|
||||
|
||||
public static function registerEventListeners(): void
|
||||
{
|
||||
foreach (LibreNMSConfig::get('auth.socialite.configs', []) as $provider => $config) {
|
||||
// Treat not set as "disabled"
|
||||
if (! isset($config['listener'])) {
|
||||
continue;
|
||||
}
|
||||
$listener = $config['listener'];
|
||||
|
||||
if (class_exists($listener)) {
|
||||
Event::listen(\SocialiteProviders\Manager\SocialiteWasCalled::class, "$listener@handle");
|
||||
} else {
|
||||
Log::error("Wrong value for auth.socialite.configs.$provider.listener set, class: '$listener' does not exist!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RedirectResponse|\Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function redirect(Request $request, string $provider)
|
||||
{
|
||||
// Re-store target url since it will be forgotten after the redirect
|
||||
$request->session()->put('url.intended', redirect()->intended()->getTargetUrl());
|
||||
|
||||
return Socialite::driver($provider)->redirect();
|
||||
}
|
||||
|
||||
public function callback(Request $request, string $provider): RedirectResponse
|
||||
{
|
||||
$this->socialite_user = Socialite::driver($provider)->user();
|
||||
|
||||
// If we already have a valid session, user is trying to pair their account
|
||||
if (Auth::user()) {
|
||||
return $this->pairUser($provider);
|
||||
}
|
||||
|
||||
$this->register($provider);
|
||||
|
||||
return $this->login($provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata endpoint used in SAML
|
||||
*/
|
||||
public function metadata(Request $request, string $provider): \Illuminate\Http\Response
|
||||
{
|
||||
$socialite = Socialite::driver($provider);
|
||||
|
||||
if (method_exists($socialite, 'getServiceProviderMetadata')) {
|
||||
return $socialite->getServiceProviderMetadata();
|
||||
}
|
||||
|
||||
return abort(404);
|
||||
}
|
||||
|
||||
private function login(string $provider): RedirectResponse
|
||||
{
|
||||
$user = User::where('auth_type', "socialite_$provider")
|
||||
->where('auth_id', $this->socialite_user->getId())
|
||||
->first();
|
||||
|
||||
try {
|
||||
if (! $user) {
|
||||
throw new AuthenticationException();
|
||||
}
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
return redirect()->intended();
|
||||
} catch (AuthenticationException $e) {
|
||||
flash()->addError($e->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
private function register(string $provider): void
|
||||
{
|
||||
if (! LibreNMSConfig::get('auth.socialite.register', false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = User::firstOrNew([
|
||||
'auth_type' => "socialite_$provider",
|
||||
'auth_id' => $this->socialite_user->getId(),
|
||||
]);
|
||||
|
||||
if ($user->user_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user->username = $this->buildUsername();
|
||||
$user->email = $this->socialite_user->getEmail();
|
||||
$user->realname = $this->buildRealName();
|
||||
|
||||
$user->save();
|
||||
}
|
||||
|
||||
private function pairUser(string $provider): RedirectResponse
|
||||
{
|
||||
$user = Auth::user();
|
||||
$user->auth_type = "socialite_$provider";
|
||||
$user->auth_id = $this->socialite_user->getId();
|
||||
|
||||
$user->save();
|
||||
|
||||
return redirect()->route('preferences.index');
|
||||
}
|
||||
|
||||
private function buildUsername(): string
|
||||
{
|
||||
return $this->socialite_user->getNickname()
|
||||
?: $this->socialite_user->getEmail()
|
||||
?: $this->buildRealName();
|
||||
}
|
||||
|
||||
private function buildRealName(): string
|
||||
{
|
||||
$name = '';
|
||||
|
||||
// These methods only exist for a few providers
|
||||
if (method_exists($this->socialite_user, 'getFirstName')) {
|
||||
$name = $this->socialite_user->getFirstName();
|
||||
}
|
||||
|
||||
if (method_exists($this->socialite_user, 'getLastName')) {
|
||||
$name = trim($name . ' ' . $this->socialite_user->getLastName());
|
||||
}
|
||||
|
||||
if (empty($name)) {
|
||||
$name = $this->socialite_user->getName();
|
||||
}
|
||||
|
||||
return ! empty($name) ? $name : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the config from Librenms Config, and insert it into Laravel Config
|
||||
*/
|
||||
private function injectConfig(): void
|
||||
{
|
||||
foreach (LibreNMSConfig::get('auth.socialite.configs', []) as $provider => $config) {
|
||||
Config::set("services.$provider", $config);
|
||||
|
||||
// Inject redirect URL automatically if not set
|
||||
if (! Config::has("services.$provider.redirect")) {
|
||||
Config::set("services.$provider.redirect",
|
||||
route('socialite.callback', [$provider])
|
||||
);
|
||||
}
|
||||
|
||||
// Inject SAML redirect url automatically
|
||||
$this->injectSAML2Config($provider);
|
||||
}
|
||||
}
|
||||
|
||||
private function injectSAML2Config(string $provider): void
|
||||
{
|
||||
if ($provider !== 'saml2') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! Config::has("services.$provider.sp_acs")) {
|
||||
Config::set("services.$provider.sp_acs", route('socialite.callback', [$provider]));
|
||||
}
|
||||
|
||||
if (! Config::has("services.$provider.client_id")) {
|
||||
Config::set("services.$provider.client_id", '');
|
||||
}
|
||||
|
||||
if (! Config::has("services.$provider.client_secret")) {
|
||||
Config::set("services.$provider.client_secret", '');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ class UserController extends Controller
|
||||
$user = User::create($user);
|
||||
|
||||
$user->setPassword($request->new_password);
|
||||
$user->auth_id = LegacyAuth::get()->getUserid($user->username) ?: $user->user_id;
|
||||
$user->auth_id = (string) LegacyAuth::get()->getUserid($user->username) ?: $user->user_id;
|
||||
$this->updateDashboard($user, $request->get('dashboard'));
|
||||
|
||||
if ($user->save()) {
|
||||
|
||||
@@ -12,6 +12,6 @@ class VerifyCsrfToken extends Middleware
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
// '*', // FIXME: CSRF completely disabled!
|
||||
'/auth/*/callback',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
|
||||
$this->app->booted('\LibreNMS\DB\Eloquent::initLegacyListeners');
|
||||
$this->app->booted('\LibreNMS\Config::load');
|
||||
$this->app->booted('\App\Http\Controllers\Auth\SocialiteController::registerEventListeners');
|
||||
|
||||
$this->bootCustomBladeDirectives();
|
||||
$this->bootCustomValidators();
|
||||
@@ -174,5 +175,23 @@ class AppServiceProvider extends ServiceProvider
|
||||
|
||||
return $validator->passes();
|
||||
}, trans('validation.exists'));
|
||||
|
||||
Validator::extend('url_or_xml', function ($attribute, $value): bool {
|
||||
if (! is_string($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter_var($value, FILTER_VALIDATE_URL) !== false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
$xml = simplexml_load_string($value);
|
||||
if ($xml !== false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ class LegacyUserProvider implements UserProvider
|
||||
/** @var User $user */
|
||||
$user->fill($new_user); // fill all attributes
|
||||
$user->auth_type = $type; // doing this here in case it was null (legacy)
|
||||
$user->auth_id = $auth_id;
|
||||
$user->auth_id = (string) $auth_id;
|
||||
$user->save();
|
||||
|
||||
return $user;
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"php-flasher/flasher-laravel": "^0.9",
|
||||
"phpmailer/phpmailer": "~6.0",
|
||||
"predis/predis": "^1.1",
|
||||
"socialiteproviders/manager": "^4.1",
|
||||
"symfony/yaml": "^4.0",
|
||||
"tecnickcom/tcpdf": "^6.4",
|
||||
"tightenco/ziggy": "^0.9"
|
||||
|
||||
@@ -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": "17d76cfe55a8adb13cb6df7c52662b86",
|
||||
"content-hash": "b4fa65afc5e0f75e6f171f432932cb75",
|
||||
"packages": [
|
||||
{
|
||||
"name": "amenadiel/jpgraph",
|
||||
@@ -2188,6 +2188,75 @@
|
||||
},
|
||||
"time": "2021-11-30T15:53:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/socialite",
|
||||
"version": "v5.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/socialite.git",
|
||||
"reference": "cb5b5538c207efa19aa5d7f46cd76acb03ec3055"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/socialite/zipball/cb5b5538c207efa19aa5d7f46cd76acb03ec3055",
|
||||
"reference": "cb5b5538c207efa19aa5d7f46cd76acb03ec3055",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^6.0|^7.0",
|
||||
"illuminate/http": "^6.0|^7.0|^8.0|^9.0",
|
||||
"illuminate/support": "^6.0|^7.0|^8.0|^9.0",
|
||||
"league/oauth1-client": "^1.0",
|
||||
"php": "^7.2|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"illuminate/contracts": "^6.0|^7.0|^8.0|^9.0",
|
||||
"mockery/mockery": "^1.0",
|
||||
"orchestra/testbench": "^4.0|^5.0|^6.0|^7.0",
|
||||
"phpunit/phpunit": "^8.0|^9.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.x-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Socialite\\SocialiteServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Socialite": "Laravel\\Socialite\\Facades\\Socialite"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Socialite\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "[email protected]"
|
||||
}
|
||||
],
|
||||
"description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.",
|
||||
"homepage": "https://laravel.com",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"oauth"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/socialite/issues",
|
||||
"source": "https://github.com/laravel/socialite"
|
||||
},
|
||||
"time": "2022-02-01T16:31:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/tinker",
|
||||
"version": "v2.7.0",
|
||||
@@ -2560,6 +2629,82 @@
|
||||
],
|
||||
"time": "2021-11-21T11:48:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth1-client",
|
||||
"version": "v1.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth1-client.git",
|
||||
"reference": "88dd16b0cff68eb9167bfc849707d2c40ad91ddc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/88dd16b0cff68eb9167bfc849707d2c40ad91ddc",
|
||||
"reference": "88dd16b0cff68eb9167bfc849707d2c40ad91ddc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-openssl": "*",
|
||||
"guzzlehttp/guzzle": "^6.0|^7.0",
|
||||
"guzzlehttp/psr7": "^1.7|^2.0",
|
||||
"php": ">=7.1||>=8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-simplexml": "*",
|
||||
"friendsofphp/php-cs-fixer": "^2.17",
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"phpstan/phpstan": "^0.12.42",
|
||||
"phpunit/phpunit": "^7.5||9.5"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-simplexml": "For decoding XML-based responses."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev",
|
||||
"dev-develop": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth1\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ben Corlett",
|
||||
"email": "[email protected]",
|
||||
"homepage": "http://www.webcomm.com.au",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "OAuth 1.0 Client Library",
|
||||
"keywords": [
|
||||
"Authentication",
|
||||
"SSO",
|
||||
"authorization",
|
||||
"bitbucket",
|
||||
"identity",
|
||||
"idp",
|
||||
"oauth",
|
||||
"oauth1",
|
||||
"single sign on",
|
||||
"trello",
|
||||
"tumblr",
|
||||
"twitter"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/oauth1-client/issues",
|
||||
"source": "https://github.com/thephpleague/oauth1-client/tree/v1.10.0"
|
||||
},
|
||||
"time": "2021-08-15T23:05:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "librenms/laravel-vue-i18n-generator",
|
||||
"version": "0.1.47",
|
||||
@@ -4657,6 +4802,80 @@
|
||||
],
|
||||
"time": "2021-09-25T23:10:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "socialiteproviders/manager",
|
||||
"version": "v4.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/SocialiteProviders/Manager.git",
|
||||
"reference": "4e63afbd26dc45ff263591de2a0970436a6a0bf9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/4e63afbd26dc45ff263591de2a0970436a6a0bf9",
|
||||
"reference": "4e63afbd26dc45ff263591de2a0970436a6a0bf9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/support": "^6.0 || ^7.0 || ^8.0 || ^9.0",
|
||||
"laravel/socialite": "~4.0 || ~5.0",
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.2",
|
||||
"phpunit/phpunit": "^6.0 || ^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"SocialiteProviders\\Manager\\ServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SocialiteProviders\\Manager\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andy Wendt",
|
||||
"email": "[email protected]"
|
||||
},
|
||||
{
|
||||
"name": "Anton Komarev",
|
||||
"email": "[email protected]"
|
||||
},
|
||||
{
|
||||
"name": "Miguel Piedrafita",
|
||||
"email": "[email protected]"
|
||||
},
|
||||
{
|
||||
"name": "atymic",
|
||||
"email": "[email protected]",
|
||||
"homepage": "https://atymic.dev"
|
||||
}
|
||||
],
|
||||
"description": "Easily add new or override built-in providers in Laravel Socialite.",
|
||||
"homepage": "https://socialiteproviders.com",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"manager",
|
||||
"oauth",
|
||||
"providers",
|
||||
"socialite"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/socialiteproviders/manager/issues",
|
||||
"source": "https://github.com/socialiteproviders/manager"
|
||||
},
|
||||
"time": "2022-01-23T22:40:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spomky-labs/base64url",
|
||||
"version": "v2.0.4",
|
||||
|
||||
@@ -170,6 +170,11 @@ return [
|
||||
Illuminate\Validation\ValidationServiceProvider::class,
|
||||
Illuminate\View\ViewServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Package Service Providers...
|
||||
*/
|
||||
\SocialiteProviders\Manager\ServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
|
||||
@@ -204,6 +204,6 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'same_site' => 'lax',
|
||||
'same_site' => env('SESSION_SAME_SITE_COOKIE', 'lax'),
|
||||
|
||||
];
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class IncreaseAuthIdLength extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('auth_id')->nullable()->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->integer('auth_id')->nullable()->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
# OAuth and SAML Support
|
||||
|
||||
## Introduction
|
||||
|
||||
LibreNMS has support for [Laravel Socialite](https://github.com/laravel/socialite) to try and simplify the use of OAuth 1 or 2 providers such as using GitHub, Microsoft, Twitter + many more and SAML.
|
||||
|
||||
[Socialite Providers](https://socialiteproviders.com) supports more than 100+ 3rd parties so you will most likely find support for the SAML or OAuth provider you need without too much trouble.
|
||||
|
||||
Please do note however, these providers are not maintained by LibreNMS so we cannot add support for new ones and we can only provide you basic help with general configuration.
|
||||
See the Socialite Providers website for more information on adding a new OAuth provider.
|
||||
|
||||
Below we will guide you on how to install SAML or some of these OAth providers, you should be able to use these as a guide on how to install any others you may need but **please, please, ensure you read the Socialite Providers documentation carefully**.
|
||||
|
||||
[GitHub Provider](https://socialiteproviders.com/GitHub/)
|
||||
[Microsoft Provider](https://socialiteproviders.com/Microsoft/)
|
||||
[SAML2](https://socialiteproviders.com/Saml2/)
|
||||
|
||||
## Requirements
|
||||
|
||||
LibreNMS version 22.3.0 or later.
|
||||
|
||||
Please ensure you set `APP_URL` within your `.env` file so that callback URLs work correctly with the identify provider.
|
||||
|
||||
!!! note
|
||||
Once you have configured your OAuth or SAML2 provider, please ensure you check the [Post configuration settings](#post-configration-settings) section at the end.
|
||||
|
||||
## GitHub and Microsoft Examples
|
||||
|
||||
### Install plugin
|
||||
|
||||
!!! note
|
||||
First we need to install the plugin itself. The plugin name can be slightly different so be sure to check the Socialite Providers documentation and look for this line, `composer require socialiteproviders/github` which will give you the name you need for the command, i.e: `socialiteproviders/github`.
|
||||
|
||||
=== "GitHub"
|
||||
|
||||
`lnms plugin:add socialiteproviders/github`
|
||||
|
||||
=== "Microsoft"
|
||||
|
||||
`lnms plugin:add socialiteproviders/microsoft`
|
||||
|
||||
### Find the provider name
|
||||
|
||||
Next we need to find the provider name and writing it down
|
||||
|
||||
!!! note
|
||||
It's almost always the name of the provider in lowercase but can be different so check the Socialite Providers documentation and look for this line, `github => [` which will give you the name you need for the above command: `github`.
|
||||
|
||||
=== "GitHub"
|
||||
|
||||
For GitHub we can find the line:
|
||||
```php
|
||||
'github' => [
|
||||
'client_id' => env('GITHUB_CLIENT_ID'),
|
||||
'client_secret' => env('GITHUB_CLIENT_SECRET'),
|
||||
'redirect' => env('GITHUB_REDIRECT_URI')
|
||||
],
|
||||
```
|
||||
So our provider name is `github`, write this down.
|
||||
|
||||
|
||||
=== "Microsoft"
|
||||
|
||||
For Microsoft we can find the line:
|
||||
```php
|
||||
'microsoft' => [
|
||||
'client_id' => env('MICROSOFT_CLIENT_ID'),
|
||||
'client_secret' => env('MICROSOFT_CLIENT_SECRET'),
|
||||
'redirect' => env('MICROSOFT_REDIRECT_URI')
|
||||
],
|
||||
```
|
||||
So our provider name is `microsoft`, write this down.
|
||||
|
||||
|
||||
### Register OAuth application
|
||||
|
||||
#### Register a new application
|
||||
|
||||
Now we need some values from the OAuth provider itself, in most cases you need to register a new "OAuth application" at the providers site. This will vary from provider to provider but the process itself should be similar to the examples below.
|
||||
|
||||
!!! note
|
||||
The callback URL is always: https://*your-librenms-url*/auth/*provider*/callback
|
||||
It doesn't need to be a public available site, but it almost always needs to support TLS (https)!
|
||||
|
||||
=== "GitHub"
|
||||
For our example with GitHub we go to [GitHub Developer Settings](https://github.com/settings/developers) and press "Register a new application":
|
||||
|
||||

|
||||
|
||||
Fill out the form accordingly (with your own values):
|
||||

|
||||
|
||||
=== "Microsoft"
|
||||
For our example with Microsoft we go to ["Azure Active Directory" > "App registrations"](https://aad.portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) and press "New registration"
|
||||
|
||||

|
||||
|
||||
Fill out the form accordingly using your own values):
|
||||

|
||||
|
||||
Copy the value of the **Application (client) ID** and **Directory (tenant) ID** and save them, you will need them in the next step.
|
||||

|
||||
|
||||
#### Generate a new client secret
|
||||
|
||||
=== "GitHub"
|
||||
|
||||
Press 'Generate a new client secret' to get a new client secret.
|
||||
|
||||

|
||||
|
||||
Copy the **Client ID** and **Client secret**
|
||||
|
||||
In the example above it is:
|
||||
|
||||
**Client ID**: 7a41f1d8215640ca6b00
|
||||
**Client secret**: ea03957288edd0e590be202b239e4f0ff26b8047
|
||||
|
||||
=== "Microsoft"
|
||||
|
||||
Select Certificates & secrets under Manage.
|
||||
Select the 'New client secret' button.
|
||||
Enter a value in Description and select one of the options for Expires and select 'Add'.
|
||||
|
||||

|
||||
|
||||
Copy the client secret **Value** (not Secret ID!) before you leave this page. You will need it in the next step.
|
||||
|
||||

|
||||
|
||||
|
||||
### Saving configuration
|
||||
|
||||
Now we need to set the configuration options for your provider within LibreNMS itself. Please replace the values in the examples below with the values you collected earlier:
|
||||
|
||||
The format of the configuration string is `auth.socialite.configs.*provider name*.*value*`
|
||||
|
||||
=== "GitHub"
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.github.client_id 7a41f1d8215640ca6b00
|
||||
lnms config:set auth.socialite.configs.github.client_secret ea03957288edd0e590be202b239e4f0ff26b8047
|
||||
```
|
||||
|
||||
=== "Microsoft"
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.microsoft.client_id 7983ac13-c955-40e9-9b85-5ba27be52a52
|
||||
lnms config:set auth.socialite.configs.microsoft.client_secret J9P7Q~K2F5C.L243sqzbGj.cOOcjTBgAPak_l
|
||||
lnms config:set auth.socialite.configs.microsoft.tenant a15edc05-152d-4eb4-973c-14f1fdc57d8b
|
||||
```
|
||||
|
||||
### Add provider event listener
|
||||
|
||||
The final step is to now add an event listener.
|
||||
|
||||
!!! note
|
||||
It's important to copy exactly the right value here,
|
||||
It should begin with a `\` and end before the `::class.'@handle'`
|
||||
|
||||
=== "GitHub"
|
||||
|
||||
Find the section looking like:
|
||||
```php
|
||||
protected $listen = [
|
||||
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
|
||||
// ... other providers
|
||||
\SocialiteProviders\GitHub\GitHubExtendSocialite::class.'@handle',
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
Copy the part: `\SocialiteProviders\GitHub\GitHubExtendSocialite` and run;
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.github.listener "\SocialiteProviders\GitHub\GitHubExtendSocialite"
|
||||
```
|
||||
Don't forget the initial backslash (\\) !
|
||||
|
||||
=== "Microsoft"
|
||||
|
||||
Find the section looking like:
|
||||
```php
|
||||
protected $listen = [
|
||||
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
|
||||
// ... other providers
|
||||
\SocialiteProviders\Microsoft\MicrosoftExtendSocialite::class.'@handle',
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
Copy the part: `\SocialiteProviders\Microsoft\MicrosoftExtendSocialite` and run;
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.microsoft.listener "\SocialiteProviders\Microsoft\MicrosoftExtendSocialite"
|
||||
```
|
||||
Don't forget the initial backslash (\\) !
|
||||
|
||||
Now you are done with setting up the OAuth provider!
|
||||
If it doesn't work, please double check your configuration values by using the `config:get` command below.
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:get auth.socialite
|
||||
```
|
||||
|
||||
## SAML2 Example
|
||||
|
||||
### Install plugin
|
||||
|
||||
The first step is to install the plugin itself.
|
||||
|
||||
```bash
|
||||
lnms plugin:add socialiteproviders/saml2
|
||||
```
|
||||
|
||||
### Add configuration
|
||||
|
||||
Depending on what your identity provider (Google, Azure, ...) supports, the configuration could look different from what you see next so please use this as a rough guide.
|
||||
It is up the IdP to provide the relevant details that you will need for configuration.
|
||||
|
||||
=== "Google"
|
||||
|
||||
Go to [https://admin.google.com/ac/apps/unified](https://admin.google.com/ac/apps/unified)
|
||||
|
||||

|
||||

|
||||
|
||||
Press "DOWNLOAD METADATA" and save the file somewhere accessible by your LibreNMS server
|
||||
|
||||

|
||||
|
||||
ACS URL = https://*your-librenms-url*/auth/saml2/callback
|
||||
Entity ID = https://*your-librenms-url*/auth/saml2
|
||||
Name ID format = PERSISTANT
|
||||
Name ID = Basic Information > Primary email
|
||||
|
||||

|
||||
|
||||
|
||||
First name = http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
|
||||
Last name = http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
|
||||
Primary email = http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.saml2.metadata "$(cat /tmp/GoogleIDPMetadata.xml)"
|
||||
```
|
||||
|
||||
Alternatively, you can copy the content of the file and run it like so, this will result in the exact same result as above.
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.saml2.metadata '''<?xml version="1.0" encoding
|
||||
...
|
||||
...
|
||||
</md:EntityDescriptor>'''
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### Using an Identity Provider metadata URL
|
||||
|
||||
!!! note
|
||||
This is the prefered and easiest way, if your IdP supports it!
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.saml2.metadata https://idp.co/metadata/xml
|
||||
```
|
||||
|
||||
#### Using an Identity Provider metadata XML file
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.saml2.metadata "$(cat GoogleIDPMetadata.xml)"
|
||||
```
|
||||
|
||||
#### Manually configuring the Identity Provider with a certificate string
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.saml2.acs https://idp.co/auth/acs
|
||||
lnms config:set auth.socialite.configs.saml2.entityid http://saml.to/trust
|
||||
lnms config:set auth.socialite.configs.saml2.certificate MIIC4jCCAcqgAwIBAgIQbDO5YO....
|
||||
```
|
||||
|
||||
#### Manually configuring the Identity Provider with a certificate file
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.saml2.acs https://idp.co/auth/acs
|
||||
lnms config:set auth.socialite.configs.saml2.entityid http://saml.to/trust
|
||||
lnms config:set auth.socialite.configs.saml2.certificate "$(cat /path/to/certificate.pem)"
|
||||
```
|
||||
|
||||
### Add provider event listener
|
||||
|
||||
Now we just need to define the listener service within LibreNMS:
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:set auth.socialite.configs.saml2.listener "\SocialiteProviders\Saml2\Saml2ExtendSocialite"
|
||||
```
|
||||
|
||||
### SESSION_SAME_SITE_COOKIE
|
||||
|
||||
You most likely will need to set `SESSION_SAME_SITE_COOKIE=none` in `.env` if you use SAML2!
|
||||
|
||||
!!! note
|
||||
Don't forget to run `lnms config:clear` after you modify `.env` to flush the config cache
|
||||
|
||||
### Service provider metadata
|
||||
|
||||
Your identify provider might ask you for your Service Provider (SP) metadata.
|
||||
LibreNMS exposes all of this information from your [LibreNMS install](https://*your-librenms-url*/auth/saml2/metadata)
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
If it doesn't work, please double check your configuration values by using the `config:get` command below.
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
```bash
|
||||
lnms config:get auth.socialite
|
||||
```
|
||||
|
||||
### Redirect URL
|
||||
If you have a need to, then you can override redirect url with the following commands:
|
||||
|
||||
=== "OAuth"
|
||||
Replace `github` and the relevant URL below with your identity provider details.
|
||||
`lnms config:set auth.socialite.configs.github.redirect https://demo.librenms.org/auth/github/callback`
|
||||
|
||||
=== "SAML2"
|
||||
`lnms config:set auth.socialite.configs.saml2.sp_acs auth/saml2/callback`
|
||||
|
||||
## Post configuration settings
|
||||
|
||||
!!! setting "auth/socialite"
|
||||
From here you can configure the settings for any identity providers you have configured along with some bespoke options.
|
||||
|
||||
Redirect Login page: This setting will skip your LibreNMS login and take the end user straight to the first idP you configured.
|
||||
|
||||
Allow registration via provider: If this setting is disabled, new users signing in via the idP will not be authenticated. This setting allows a local user to be automatically created which permits their login.
|
||||
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js?id=50f82aa2aac679191fac",
|
||||
"/js/app.js": "/js/app.js?id=dba3e37f44ce826e96d0",
|
||||
"/js/manifest.js": "/js/manifest.js?id=2951ae529be231f05a93",
|
||||
"/css/vendor.css": "/css/vendor.css?id=2568831af31dbfc3128a",
|
||||
"/css/app.css": "/css/app.css?id=936fe619dcf1bac0a33f",
|
||||
"/js/vendor.js": "/js/vendor.js?id=c5fd3d75a63757080dbb",
|
||||
"/js/lang/de.js": "/js/lang/de.js?id=1aedfce25e3daad3046a",
|
||||
"/js/lang/en.js": "/js/lang/en.js?id=3617cad4c8bcf97221a6",
|
||||
"/js/lang/en.js": "/js/lang/en.js?id=f16225e77f5dbe2541ac",
|
||||
"/js/lang/fr.js": "/js/lang/fr.js?id=a20c4c78eb5f9f4a374b",
|
||||
"/js/lang/it.js": "/js/lang/it.js?id=6b0bdf3be6dc3bf0a167",
|
||||
"/js/lang/ru.js": "/js/lang/ru.js?id=f6b7c078755312a0907c",
|
||||
|
||||
@@ -372,6 +372,38 @@
|
||||
"order": 1,
|
||||
"type": "text"
|
||||
},
|
||||
"auth.socialite.redirect": {
|
||||
"group": "auth",
|
||||
"section": "socialite",
|
||||
"order": 1,
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"auth.socialite.register": {
|
||||
"group": "auth",
|
||||
"section": "socialite",
|
||||
"order": 2,
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
|
||||
"auth.socialite.configs": {
|
||||
"group": "auth",
|
||||
"section": "socialite",
|
||||
"order": 3,
|
||||
"type": "array-sub-keyed",
|
||||
"validate": {
|
||||
"value": "array",
|
||||
"value.*": "array",
|
||||
"value.*.listener": ["not_regex:/[:|@]/"],
|
||||
"value.*.listener": ["regex:/^\\\\SocialiteProviders\\\\[^\\\\]+\\\\[^\\\\]+ExtendSocialite$/"],
|
||||
"value.*.redirect": "url",
|
||||
"value.saml.metadata": "url_or_xml",
|
||||
"value.saml.acs": "url",
|
||||
"value.saml.entityid": "url"
|
||||
}
|
||||
},
|
||||
|
||||
"auth_ad_check_certificates": {
|
||||
"default": false,
|
||||
"group": "auth",
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
"text",
|
||||
"boolean",
|
||||
"array",
|
||||
"array-sub-keyed",
|
||||
"password",
|
||||
"email",
|
||||
"color",
|
||||
|
||||
@@ -2039,7 +2039,7 @@ users:
|
||||
Columns:
|
||||
- { Field: user_id, Type: 'int unsigned', 'Null': false, Extra: auto_increment }
|
||||
- { Field: auth_type, Type: varchar(32), 'Null': true, Extra: '' }
|
||||
- { Field: auth_id, Type: int, 'Null': true, Extra: '' }
|
||||
- { Field: auth_id, Type: varchar(255), 'Null': true, Extra: '' }
|
||||
- { Field: username, Type: varchar(255), 'Null': false, Extra: '' }
|
||||
- { Field: password, Type: varchar(255), 'Null': true, Extra: '' }
|
||||
- { Field: realname, Type: varchar(64), 'Null': false, Extra: '' }
|
||||
|
||||
@@ -134,6 +134,7 @@ nav:
|
||||
- Galera Database Cluster: Extensions/Galera-Cluster.md
|
||||
- IRC Bot Extensions: Extensions/IRC-Bot-Extensions.md
|
||||
- IRC Bot: Extensions/IRC-Bot.md
|
||||
- Oauth/SAML support: Extensions/OAuth-SAML.md
|
||||
- RRDCached: Extensions/RRDCached.md
|
||||
- RRDTune: Extensions/RRDTune.md
|
||||
- Scaling LibreNMS: Extensions/Distributed-Poller.md
|
||||
|
||||
@@ -7615,21 +7615,6 @@ parameters:
|
||||
count: 1
|
||||
path: app/Http/Controllers/Ajax/RipeNccApiController.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Controllers\\\\Auth\\\\LoginController\\:\\:loggedOut\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: app/Http/Controllers/Auth/LoginController.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Controllers\\\\Auth\\\\LoginController\\:\\:showLoginForm\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: app/Http/Controllers/Auth/LoginController.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Controllers\\\\Auth\\\\LoginController\\:\\:username\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
path: app/Http/Controllers/Auth/LoginController.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Controllers\\\\Auth\\\\TwoFactorController\\:\\:showTwoFactorForm\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
<!--
|
||||
- SettingArraySubKeyed.vue
|
||||
-
|
||||
- Description-
|
||||
-
|
||||
- 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 <https://www.gnu.org/licenses/>.
|
||||
-
|
||||
- @package LibreNMS
|
||||
- @link https://www.librenms.org
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div v-tooltip="disabled ? $t('settings.readonly') : false">
|
||||
<div v-for="(item, index) in localList">
|
||||
<b>{{ index }}</b>
|
||||
<div v-for="(item, subindex) in item" class="input-group">
|
||||
<span :class="['input-group-addon', disabled ? 'disabled' : '']">{{ subindex }}</span>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
:value="item"
|
||||
:readonly="disabled"
|
||||
@blur="updateSubItem(index, subindex, $event.target.value)"
|
||||
@keyup.enter="updateSubItem(index, subindex, $event.target.value)"
|
||||
>
|
||||
<span class="input-group-btn">
|
||||
<button v-if="!disabled" @click="removeSubItem(index, subindex)" type="button" class="btn btn-danger"><i class="fa fa-minus-circle"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="!disabled">
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<div class="input-group">
|
||||
<input type="text" v-model="newSubItemKey[index]" class="form-control" placeholder="Key">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8">
|
||||
<div class="input-group">
|
||||
<input type="text" v-model="newSubItemValue[index]" @keyup.enter="addSubItem(index)" class="form-control" placeholder="Value">
|
||||
<span class="input-group-btn">
|
||||
<button @click="addSubItem(index)" type="button" class="btn btn-primary"><i class="fa fa-plus-circle"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
</div>
|
||||
<div v-if="!disabled">
|
||||
<div class="input-group">
|
||||
<input type="text" v-model="newSubArray" @keyup.enter="addSubArray" class="form-control">
|
||||
<span class="input-group-btn">
|
||||
<button @click="addSubArray" type="button" class="btn btn-primary"><i class="fa fa-plus-circle"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseSetting from "./BaseSetting";
|
||||
|
||||
export default {
|
||||
name: "SettingArraySubKeyed",
|
||||
mixins: [BaseSetting],
|
||||
|
||||
data() {
|
||||
return {
|
||||
localList: this.value ?? new Object(),
|
||||
newSubItemKey: {},
|
||||
newSubItemValue: {},
|
||||
newSubArray: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addSubItem(index) {
|
||||
if (this.disabled) return;
|
||||
|
||||
var obj = {};
|
||||
obj[this.newSubItemKey[index]] = this.newSubItemValue[index];
|
||||
|
||||
if (Object.keys(this.localList[index]).length === 0) {
|
||||
this.localList[index] = new Object();
|
||||
}
|
||||
Object.assign(this.localList[index], obj);
|
||||
this.$emit('input', this.localList);
|
||||
this.newSubItemValue[index] = "";
|
||||
this.newSubItemKey[index] = "";
|
||||
},
|
||||
removeSubItem(index, subindex) {
|
||||
if (this.disabled) return;
|
||||
delete this.localList[index][subindex];
|
||||
|
||||
if (Object.keys(this.localList[index]).length === 0) {
|
||||
delete this.localList[index];
|
||||
}
|
||||
|
||||
this.$emit('input', this.localList);
|
||||
},
|
||||
updateSubItem(index, subindex, value) {
|
||||
if (this.disabled || this.localList[index][subindex] === value) return;
|
||||
this.localList[index][subindex] = value;
|
||||
this.$emit('input', this.localList);
|
||||
},
|
||||
addSubArray() {
|
||||
if (this.disabled) return;
|
||||
this.localList[this.newSubArray] = new Object();
|
||||
this.$emit('input', this.localList);
|
||||
this.newSubArray = "";
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value(updated) {
|
||||
// careful to avoid loops with this
|
||||
this.localList = updated;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.input-group {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.input-group-addon:not(.disabled) {
|
||||
cursor: move;
|
||||
}
|
||||
</style>
|
||||
@@ -30,6 +30,7 @@ return [
|
||||
'general' => ['name' => 'General Authentication Settings'],
|
||||
'ad' => ['name' => 'Active Directory Settings'],
|
||||
'ldap' => ['name' => 'LDAP Settings'],
|
||||
'socialite' => ['name' => 'Socialite Settings'],
|
||||
],
|
||||
'authorization' => [
|
||||
'device-group' => ['name' => 'Device Group Settings'],
|
||||
@@ -258,6 +259,20 @@ return [
|
||||
'astext' => [
|
||||
'description' => 'Key to hold cache of autonomous systems descriptions',
|
||||
],
|
||||
'auth' => [
|
||||
'socialite' => [
|
||||
'redirect' => [
|
||||
'description' => 'Redirect Login page',
|
||||
'help' => 'Login page should redirect immediately to the first defined provider.<br><br>TIPS: You can prevent it by appending ?redirect=0 in the url',
|
||||
],
|
||||
'register' => [
|
||||
'description' => 'Allow registration via provider',
|
||||
],
|
||||
'configs' => [
|
||||
'description' => 'Provider configs',
|
||||
],
|
||||
],
|
||||
],
|
||||
'auth_ad_base_dn' => [
|
||||
'description' => 'Base DN',
|
||||
'help' => 'groups and users must be under this dn. Example: dc=example,dc=com',
|
||||
|
||||
@@ -46,8 +46,18 @@
|
||||
<button type="submit" id="login" class="btn btn-primary btn-block" name="submit">
|
||||
<i class="fa fa-btn fa-sign-in"></i> {{ __('Login') }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@foreach (\LibreNMS\Config::get('auth.socialite.configs', []) as $provider => $config)
|
||||
<br>
|
||||
<form role="form" action="{{ route('socialite.redirect', $provider) }}" method="post">
|
||||
{{ csrf_field() }}
|
||||
<button type="submit" id="login" class="btn btn-success btn-block">
|
||||
<i class="fab fa-btn fa-{{ $provider }}"></i> {{ __('Login with') }} {{ ucfirst($provider) }}
|
||||
</button>
|
||||
</form>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-panel>
|
||||
|
||||
@@ -101,6 +101,19 @@
|
||||
</form>
|
||||
</x-panel>
|
||||
|
||||
@config('auth.socialite.configs')
|
||||
<x-panel title="{{ __('OAuth/SAML Authentication') }}">
|
||||
@foreach (\LibreNMS\Config::get('auth.socialite.configs', []) as $provider => $config)
|
||||
<form role="form" action="{{ route('socialite.redirect', $provider) }}" method="post">
|
||||
{{ csrf_field() }}
|
||||
<button type="submit" id="login" class="btn btn-success btn-block">
|
||||
<i class="fab fa-btn fa-{{ $provider }}"></i> {{ __('Register with') }} {{ ucfirst($provider) }}
|
||||
</button>
|
||||
</form>
|
||||
@endforeach
|
||||
</x-panel>
|
||||
@endconfig
|
||||
|
||||
@config('twofactor')
|
||||
<x-panel title="{{ __('Two-Factor Authentication') }}">
|
||||
@if($twofactor)
|
||||
|
||||
@@ -16,6 +16,13 @@ use Illuminate\Support\Facades\Route;
|
||||
// Auth
|
||||
Auth::routes(['register' => false, 'reset' => false, 'verify' => false]);
|
||||
|
||||
// Socialite
|
||||
Route::prefix('auth')->name('socialite.')->group(function () {
|
||||
Route::post('{provider}/redirect', [\App\Http\Controllers\Auth\SocialiteController::class, 'redirect'])->name('redirect');
|
||||
Route::match(['get', 'post'], '{provider}/callback', [\App\Http\Controllers\Auth\SocialiteController::class, 'callback'])->name('callback');
|
||||
Route::get('{provider}/metadata', [\App\Http\Controllers\Auth\SocialiteController::class, 'metadata'])->name('metadata');
|
||||
});
|
||||
|
||||
// WebUI
|
||||
Route::group(['middleware' => ['auth'], 'guard' => 'auth'], function () {
|
||||
|
||||
|
||||