Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion Events.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,73 @@

namespace humhub\modules\sso\jwt;


use humhub\modules\sso\jwt\authclient\JWT;
use humhub\modules\user\authclient\Collection;
use humhub\modules\sso\jwt\models\Configuration;
use humhub\components\Event;
use Yii;

class Events
{
/**
* JWT Handling on login page.
*
* @param Event $event The event triggering this method.
* @return void
* @throws \yii\base\InvalidConfigException If there are configuration issues.
* @throws \yii\base\InvalidParamException If parameters are invalid.
* @throws \yii\web\ServerErrorHttpException If a server error occurs.
* @since 1.1
*/
public static function onAuthClientCollectionInit($event)
{
try {
if (Yii::$app->user->isGuest &&
isset(Yii::$app->authClientCollection) &&
Yii::$app->authClientCollection->hasClient('jwt')) {

$jwtAuth = Yii::$app->authClientCollection->getClient('jwt');

if ($jwtAuth->checkIPAccess()) {
if ($jwtAuth->autoLogin &&
$event->action->id === 'login' &&
empty(Yii::$app->request->get('noJwt'))) {

$event->isValid = false;
Yii::$app->response->redirect($jwtAuth->redirectToBroker());
Yii::$app->end();
}
} else {
Yii::$app->authClientCollection->removeClient('jwt');
}
}
} catch (\Exception $e) {
Yii::error('JWT Auth Error: ' . $e->getMessage(), 'jwt-auth');
}
}

/**
* @param Event $event
*/
public static function onCollectionAfterClientsSet($event)
{
/** @var Collection $authClientCollection */
$authClientCollection = $event->sender;
if (!($authClientCollection instanceof Collection)) {
return;
}

$config = Configuration::getInstance();
if (!empty($config->enabled)) {
$authClientCollection->setClient('jwt', [
'class' => JWT::class,
'url' => $config->url,
'sharedKey' => $config->sharedKey,
'supportedAlgorithms' => $config->supportedAlgorithms,
'idAttribute' => $config->idAttribute,
'leeway' => $config->leeway,
'allowedIPs' => $config->allowedIPs
]);
}
}
}
37 changes: 14 additions & 23 deletions Module.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2019 HumHub GmbH & Co. KG
Expand All @@ -7,40 +8,30 @@

namespace humhub\modules\sso\jwt;

use humhub\components\Event;
use Yii;
use humhub\modules\sso\jwt\models\Configuration;
use yii\helpers\Url;
use Yii;

class Module extends \humhub\components\Module
{
public $resourcesPath = 'resources';

/**
* JWT Handling on login page
*
* @param Event $event
* @return void
* @throws \yii\base\InvalidConfigException
* @since 1.1
* @inheritdoc
*/
public static function onAuthClientCollectionInit($event)
public function getConfigUrl()
{
if (!Yii::$app->user->isGuest) {
return;
}
return Url::to(['/jwt-sso/admin/index']);
}

if (Yii::$app->authClientCollection->hasClient('jwt')) {
$jwtAuth = Yii::$app->authClientCollection->getClient('jwt');
private ?Configuration $configuration = null;

if ($jwtAuth->checkIPAccess()) {
if ($jwtAuth->autoLogin && $event->action->id == 'login' && empty(Yii::$app->request->get('noJwt'))) {
$event->isValid = false;
return $jwtAuth->redirectToBroker();
}
} else {
// Not allowed, remove authClient
Yii::$app->authClientCollection->removeClient('jwt');
}
public function getConfiguration(): Configuration
{
if ($this->configuration === null) {
$this->configuration = new Configuration(['settingsManager' => $this->settings]);
$this->configuration->loadBySettings();
}
return $this->configuration;
}
}
51 changes: 37 additions & 14 deletions authclient/JWT.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,55 +13,65 @@
use Yii;
use humhub\modules\user\authclient\interfaces\StandaloneAuthClient;
use humhub\modules\user\models\User;
use humhub\modules\sso\jwt\models\Configuration;

/**
* JWT Authclient
*/
class JWT extends BaseClient implements StandaloneAuthClient
{

/**
* @var string url of the JWT provider
*/
public $url = '';
public $url;

/**
* @var string shared key
*/
public $sharedKey = '';
public $sharedKey;

/**
* @var array a list of supported jwt verification algorithms Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
*/
public $supportedAlgorithms = ['HS256'];
public $supportedAlgorithms;

/**
* @var string attribute to match user tables with (email, username, id, guid)
*/
public $idAttribute = 'email';
public $idAttribute;

/**
* @var int token time leeway
*/
public $leeway = 60;
public $leeway;

/**
* @var array the list of IPs that are allowed to use JWT.
* Each array element represents a single IP filter which can be either an IP address
* or an address with wildcard (e.g. 192.168.0.*) to represent a network segment.
*/
public $allowedIPs = [];
public $allowedIPs;

/**
* @var boolean enable automatic login of 'allowed ips'.
*/
public $autoLogin = false;
public $autoLogin;

/**
* @inheritdoc
*/
public function init()
{
$config = Configuration::getInstance();

$this->url = $config->url;
$this->sharedKey = $config->sharedKey;
$this->supportedAlgorithms = $config->supportedAlgorithms;
$this->idAttribute = $config->idAttribute;
$this->leeway = $config->leeway;
$this->allowedIPs = $config->allowedIPs;
$this->autoLogin = $config->autoLogin;

parent::init();
Yii::setAlias('@Firebase/JWT', '@jwt-sso/vendors/php-jwt/src');
}
Expand All @@ -85,14 +95,14 @@ public function authAction($authAction)
\Firebase\JWT\JWT::$leeway = $this->leeway;
$decodedJWT = \Firebase\JWT\JWT::decode($token, $this->sharedKey, $this->supportedAlgorithms);
} catch (\Exception $ex) {
Yii::error("JWT decode error: " . $ex->getMessage(), 'jwt-auth');
Yii::$app->session->setFlash('error', Yii::t('JwtSsoModule.jwt', $ex->getMessage()));
return Yii::$app->getResponse()->redirect(['/user/auth/login']);
}

$this->setUserAttributes((array)$decodedJWT);
$this->autoStoreAuthClient();


return $authAction->authSuccess($this);
}

Expand All @@ -109,16 +119,22 @@ public function setUserAttributes($userAttributes)
if (!isset($userAttributes['id'])) {
if ($this->idAttribute == 'email' && isset($userAttributes['email'])) {
$userAttributes['id'] = $userAttributes['email'];
} else if ($this->idAttribute == 'guid' && isset($userAttributes['guid'])) {
$userAttributes['guid'] = $userAttributes['guid'];
} else if ($this->idAttribute == 'username' && isset($userAttributes['username'])) {
$userAttributes['username'] = $userAttributes['username'];
} elseif ($this->idAttribute == 'guid' && isset($userAttributes['guid'])) {
$userAttributes['id'] = $userAttributes['guid'];
} elseif ($this->idAttribute == 'username' && isset($userAttributes['username'])) {
$userAttributes['id'] = $userAttributes['username'];
} else {
Yii::warning("Unable to set user ID attribute. idAttribute: {$this->idAttribute}", 'jwt-auth');
}
}

return parent::setUserAttributes($userAttributes);
}

/**
* Redirects the user to the JWT broker
* @return \yii\web\Response the response object
*/
public function redirectToBroker()
{
return Yii::$app->getResponse()->redirect($this->url);
Expand Down Expand Up @@ -160,6 +176,8 @@ protected function autoStoreAuthClient()
$user = $this->getUserByAttributes();
if ($user !== null) {
(new AuthClientUserService($user))->add($this);
} else {
Yii::warning("No user found for auto-storing auth client", 'jwt-auth');
}
}

Expand All @@ -173,9 +191,14 @@ protected function getUserByAttributes()
return User::findOne(['email' => $attributes['email']]);
}

Yii::warning("No email found in user attributes", 'jwt-auth');
return null;
}

/**
* Checks if the current IP is allowed to use JWT
* @return bool whether the current IP is allowed
*/
public function checkIPAccess()
{
if (empty($this->allowedIPs)) {
Expand All @@ -188,7 +211,7 @@ public function checkIPAccess()
return true;
}
}
Yii::warning("IP access denied for: " . $ip, 'jwt-auth');
return false;
}

}
6 changes: 4 additions & 2 deletions config.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
/** @noinspection MissedFieldInspection */

use humhub\modules\user\controllers\AuthController;
use humhub\modules\user\authclient\Collection;

return [
'id' => 'jwt-sso',
'class' => 'humhub\modules\sso\jwt\Module',
'namespace' => 'humhub\modules\sso\jwt',
'events' => [
[AuthController::class, AuthController::EVENT_BEFORE_ACTION, ['humhub\modules\sso\jwt\Module', 'onAuthClientCollectionInit']],
[Collection::class, Collection::EVENT_AFTER_CLIENTS_SET, ['humhub\modules\sso\jwt\Events', 'onCollectionAfterClientsSet']],
[AuthController::class, AuthController::EVENT_BEFORE_ACTION, ['humhub\modules\sso\jwt\Events', 'onAuthClientCollectionInit']],
]
];
?>
?>
28 changes: 28 additions & 0 deletions controllers/AdminController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace humhub\modules\sso\jwt\controllers;

use Yii;
use humhub\modules\admin\components\Controller;
use humhub\modules\sso\jwt\models\Configuration;
use humhub\modules\sso\jwt\Module;

/**
* Module configuation
*/
class AdminController extends Controller
{

public function actionIndex()
{
$model = $this->module->getConfiguration();

if ($model->load(Yii::$app->request->post()) && $model->save()) {
$this->view->saved();
return $this->redirect(['index']);
}

return $this->render('index', ['model' => $model]);
}

}
Loading