Add support for Okta Group claims to set Roles (#15592)

* Add support for Okta Group claims to set Roles

* styleci changes

* make Stan happy
This commit is contained in:
Peter Childs
2023-11-28 13:08:41 +10:30
committed by GitHub
parent d61c71bcbe
commit 90927be973
4 changed files with 138 additions and 1 deletions

View File

@@ -27,6 +27,7 @@ use App\Models\User;
use Config;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Contracts\User as SocialiteUser;
use Laravel\Socialite\Facades\Socialite;
@@ -52,11 +53,32 @@ class SocialiteController extends Controller
// 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();
$driver = Socialite::driver($provider);
// https://laravel.com/docs/10.x/socialite#access-scopes
if ($driver instanceof \Laravel\Socialite\Two\AbstractProvider) {
$scopes = LibreNMSConfig::get('auth.socialite.scopes');
if (! empty($scopes) && is_array($scopes)) {
return $driver
->scopes($scopes)
->redirect();
}
}
return $driver->redirect();
}
public function callback(Request $request, string $provider): RedirectResponse
{
/* If we get an error in the callback then attempt to handle nicely */
if (array_key_exists('error', $request->query())) {
$error = $request->query('error');
$error_description = $request->query('error_description');
flash()->addError($error . ': ' . $error_description);
return redirect()->route('login');
}
$this->socialite_user = Socialite::driver($provider)->user();
// If we already have a valid session, user is trying to pair their account
@@ -95,6 +117,7 @@ class SocialiteController extends Controller
}
Auth::login($user);
$this->setRolesFromClaim($provider, $user);
return redirect()->intended();
} catch (AuthenticationException $e) {
@@ -126,6 +149,40 @@ class SocialiteController extends Controller
$user->save();
}
private function setRolesFromClaim(string $provider, $user): bool
{
$scopes = LibreNMSConfig::get('auth.socialite.scopes');
$claims = LibreNMSConfig::get('auth.socialite.claims');
$default_role = LibreNMSConfig::get('auth.socialite.default_role');
if (is_array($scopes) &&
$this->socialite_user instanceof \Laravel\Socialite\AbstractUser &&
! empty($claims)
) {
$roles = [];
$attributes = $this->socialite_user->getRaw();
foreach ($scopes as $scope) {
foreach (Arr::wrap($attributes[$scope] ?? []) as $scope_data) {
$roles = array_merge($roles, $claims[$scope_data]['roles'] ?? []);
}
}
if (count($roles) > 0) {
$user->setRoles(array_unique($roles), true);
return true;
}
}
if ($default_role !== null && $default_role != 'none') {
$user->setRoles([$default_role], false);
return true;
}
return false;
}
private function pairUser(string $provider): RedirectResponse
{
$user = Auth::user();

View File

@@ -271,6 +271,47 @@ If it doesn't work, please double check your configuration values by using the `
lnms config:get auth.socialite
```
### Default Role
Since most Socialite Providers don't provide Authorization only Authentication it is possible to set
the default User Role for Authorized users. Appropriate care should be taken.
- none: **No Access**: User has no access
- normal: **Normal User**: You will need to assign device / port
permissions for users at this level.
- global-read: **Global Read**: Read only Administrator.
- admin: **Administrator**: This is a global read/write admin account.
!!! setting "settings/auth/socialite"
```bash
lnms config:set auth.socialite.default_role global-read
```
### Claims / Access Scopes
Socialite can specifiy scopes that should be included with in the authentication request.
(see https://laravel.com/docs/10.x/socialite#access-scopes )
For example, if Okta is configured to expose group information it is possible to use these group
names to configure User Roles.
First enable sending the 'groups' claim (along with the normal openid, profile, and email claims )
!!! setting "settings/auth/socialite"
```bash
lnms config:set auth.socialite.scopes.+ groups
```
Then setup mappings from the returned claim arrays to the User levels you want
!!! setting "settings/auth/socialite"
```bash
lnms config:set auth.socialite.claims.RETURN_FROM_CLAIM.roles '["admin"]'
lnms config:set auth.socialite.claims.OTHER_RETURN_FROM_CLAIM.roles '["global-read","cleaner"]'
```
## SAML2 Example
### Install plugin

View File

@@ -282,6 +282,10 @@ return [
'configs' => [
'description' => 'Provider configs',
],
'scopes' => [
'description' => 'Scopes that should be included with in the authentication request',
'help' => 'See https://laravel.com/docs/10.x/socialite#access-scopes',
],
],
],
'auth_ad_base_dn' => [

View File

@@ -430,6 +430,41 @@
"value.saml.entityid": "url"
}
},
"auth.socialite.scopes": {
"group": "auth",
"section": "socialite",
"order": 4,
"type": "array"
},
"auth.socialite.default_role": {
"group": "auth",
"section": "socialite",
"order": 5,
"type": "select",
"default": "none",
"options" : {
"none" : "No Access",
"normal" : "Normal Access",
"global-read" : "Global Viewing Access",
"admin" : "Global Administrative Access"
}
},
"auth.socialite.claims": {
"default": {},
"group": "auth",
"section": "socialite",
"order": 6,
"type": "group-role-map",
"options": {
"groupPlaceholder": "Claim Group"
},
"validate": {
"value": "array",
"value.*": "array",
"value.*.roles": "array|required",
"value.*.roles.*": "string"
}
},
"auth_ad_base_dn": {
"group": "auth",
"section": "ad",