forked from YesWiki/yeswiki-extension-login-sso
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from YesWiki/coopaname-integration
Coopaname integration
- Loading branch information
Showing
19 changed files
with
783 additions
and
493 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Changelog | ||
|
||
## V1.0.0 | ||
|
||
This extension is a replacement for the yeswiki-extension-login-sso, | ||
due to an incompatibility between dash in the extension name and the new autoloading YesWiki system. | ||
It also refactor the code to use the new YesWiki class based action system and twig templates. | ||
|
||
Then it add a few new features for better SSO integration : | ||
|
||
- Add a new configuration option 'id_sso_field' to link the user between the SSO server and the YesWiki user and avoid using email as primary key if wanted | ||
- Support for email update on SSO server side if another key is used to link the user | ||
- Use a fixed URL for callback from server, increasing compatibility with LemonLDAP::NG | ||
- Allow group mapping from SSO groups to YesWiki groups |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,157 @@ | ||
# yeswiki-extension-login-sso | ||
# yeswiki-extension-loginsso | ||
|
||
Use SSO server authentification in YesWiki | ||
Use SSO OIDC server authentification in YesWiki. | ||
|
||
See the example of configuration given in the _config.yaml_ | ||
This extension replace defaut user/password login. | ||
|
||
Once copied in the *tools* folder, finish the install by typing `composer install`. | ||
|
||
__TODO__ merge with yeswiki-extension-login-cas to permit the connection with a CAS server | ||
## Add configuration option | ||
After installation, you must add the following configuration in your waka.config.php file and configure according to your needs | ||
|
||
```php | ||
'sso_config' => [ | ||
/* | ||
* The form id for the bazar entry corresponding to the connected user | ||
* if defined, a link propose to show him his user information (profile) | ||
* don't declare it, if you don't need to have bazar entries related to users | ||
*/ | ||
'bazar_user_entry_id' => 1000, | ||
// each entry here is an array corresponding to a SSO provider | ||
'providers' => [ | ||
[ | ||
// the authentification auth type, two protocols are supported: 'oauth2' and 'cas' | ||
'auth_type' => 'oauth2', | ||
'auth_options' => [ | ||
'clientId' => 'myclientid', | ||
'clientSecret' => 'mysecretclientkey', | ||
'urlAuthorize' => 'https:#myserver/auth/realms/master/protocol/openid-connect/auth', | ||
'urlAccessToken ' => 'https:#myserver/auth/realms/master/protocol/openid-connect/token', | ||
'urlResourceOwnerDetails' => 'https:#myserver/auth/realms/master/protocol/openid-connect/userinfo', | ||
], | ||
// sso server fieldname used for the user id, this field links an SSO user to a yeswiki user | ||
'id_sso_field' => 'id', | ||
// sso server fieldname used for the user email | ||
'email_sso_field' => 'email', | ||
// sso server fieldname used for the user groups | ||
'groups_sso_field' => 'groups', | ||
// map LDAP groups to YesWiki groups. Groups not listed here will be ignored | ||
'groups_sso_mapping' => [ | ||
'group_ldap' => 'group_wiki' | ||
], | ||
/* | ||
* if create_user_from is defined, an yeswiki user with a name and an email is created. | ||
* the username is an unique word (ID) generated from the format create_user_form by specifying #[field_name] to referring to a sso field | ||
* if not defined, the authentification module accepts only sso users which have an yeswiki user corresponding to this email | ||
* for example, '#[given_name] #[family_name]' sets the full name of a person assuming that the two fields have been defined in the sso user information | ||
* for 'Jean Dupond' it creates a 'JeanDupond' identifier, and if one already exists in the database, it sets 'JeanDupond2' | ||
*/ | ||
'create_user_from' => '#[given_name] #[family_name]', | ||
// style of the login button which corresponds to the provider | ||
'button_style' => [ | ||
// name used for the login button | ||
'button_label' => 'My Auth Server', | ||
// class of this button | ||
'button_class' => 'btn btn-default btn-myauth', | ||
// icon used for this button (class of the <i>) | ||
'button_icon' => 'glyphicon glyphicon-log-in' | ||
], | ||
/* you can also write a wiki page named 'ConnectionDetails' to inform the user before the buttons are displayed | ||
* if bazar_mapping is defined, the module will create an bazar entry for the user at his first connection | ||
* in this case, bazar_user_entry_id needs to be defined | ||
*/ | ||
'bazar_mapping' => [ | ||
// the fields mapping between the yeswiki entry fields and the sso fields | ||
'fields' => [ | ||
/* | ||
* mapping: 'yeswiki entry fieldname: 'SSO server fieldname' | ||
* you can't define 'bf_title' because it will automatically value determined by the above 'create_user_from' but contrary to the username, | ||
* the it's not an identifier create from this value | ||
*/ | ||
'bf_nom' => 'family_name', | ||
'bf_prenom' => 'given_name', | ||
'bf_email' => 'email' | ||
], | ||
/* | ||
* if some fields need to be transformed in an other format, you can defined an associative array of each transformation | ||
* no transformation will be made if the user has chosen to anonymize its data | ||
* each element must have the 'yeswiki_entry_field', 'sso_field', 'pattern' and 'replacement' keys | ||
*/ | ||
'fields_transformed' => [ | ||
// the first transformation | ||
[ | ||
/* | ||
* this example transforms an address like '[email protected]' to this address: 'myadress|myserver.com' | ||
* the yeswiki entry fieldname which be the result of the transformation, it MUST BE already defined as a key in bazar_mapping | ||
*/ | ||
'yeswiki_entry_field' => 'bf_email', | ||
/* | ||
* the sso fieldname used for the transformation, it has to be defined in the sso user information but doesn't need to be declared | ||
* as a value in 'bazar_mappings.fields' | ||
*/ | ||
'sso_field' => 'email', | ||
/* | ||
* regular expression pattern which match with the value of the sso_field, it has to extract some groups used in replacement | ||
* see https:#www.php.net/manual/en/function.preg-replace.php for more details about the syntax | ||
*/ | ||
'pattern' => '/([ a-z\-_\. ]+)@([ a-z\-_\. ]+)/i', | ||
/* '/\s*(\d+\.\d*)\s*,\s*(\d+\.\d*)\s/' | ||
* the pattern which defines the final value set for the yeswiki entry field by referring the pattern groups | ||
* see https:#www.php.net/manual/en/function.preg-replace.php for more details about the syntax | ||
*/ | ||
'replacement' => '$1|$2' | ||
], | ||
// access defined to view the user entry ('+' by default) | ||
'read_access_entry' => '+', | ||
// access defined to modify the user entry ('%' by default) | ||
'write_access_entry' => '%', | ||
// message displayed before to create the user entry | ||
'entry_creation_information' => "<p>C'est votre première connexion avec ce compte. Une fiche avec vos informations personnelles va être créée dans le but de faciliter la mise en | ||
lien entre les utilisateurs. Les données suivantes - Prénom, Nom, E-mail - vont êtres récupérées directement depuis le serveur d'authentification et pourront être modifiées | ||
ou supprimées plus tard à votre convenance dans 'Mes fiches'.</p>", | ||
// if anonymize is defined, a question is asked before to know if the user wants to be anonymous | ||
'anonymize' => [ | ||
// consent question asked before the creation of the user entry (html tag are allowed), if the user responds 'no' his data will be transformed as below | ||
'consent_question' => "<p>Acceptez-vous que ces informations personnelles soient utilisées sur ce site ?<br> | ||
Si oui, ces données seront sauvées et rendues visibles aux autres utilisateurs (sauf le mail).<br> | ||
Si vous refusez, seules vos initiales et votre pseudo de connexion seront inscrits dans votre fiche, et votre mail sera sauvegardé mais caché aux autres utilisateurs.</p> | ||
<p>Nous rappelons aussi que nous ne faisons rien d'autre de ces données que de les afficher sur la fiche de votre profil (pas de revente, ni d'exploitation).</p>", | ||
/* | ||
* only the first character will be kept for the followed fields | ||
* each field refers to yeswiki entry field and MUST BE already defined as a key in bazar_mapping.fields | ||
*/ | ||
'fields_to_anonymize' => [ | ||
'bf_nom', | ||
'bf_prenom' | ||
], | ||
/* | ||
* all the content will be copied for the followed fields | ||
* each field refers to the yeswiki entry field and MUST BE already defined as a key in bazar_mapping.fields | ||
*/ | ||
'fields_to_keep' => [ | ||
'bf_email' | ||
], | ||
/* | ||
* in case of an anonymize user, 'bf_titre' is replaced by this value | ||
* the username will also be also with an unique word (id) according to this value. Per example, for the value 'Utilisateur anonyme', | ||
* we will have the following ids: first 'UtilisateurAnonyme', then 'UtilisateurAnonyme2' for the second user, etc. | ||
*/ | ||
'bf_titre_value' => 'Utilisateur anonyme' | ||
] | ||
] | ||
] | ||
] | ||
] | ||
] | ||
``` | ||
|
||
## Configure the OIDC server | ||
|
||
You must configure the OIDC server to accept the redirection from your YesWiki instance. | ||
Add the following URL to the list of allowed redirections: | ||
`https://[wiki]/?api/auth_sso/callback=` | ||
|
||
## TODO | ||
|
||
- merge with yeswiki-extension-login-cas to permit the connection with a CAS server | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
<?php | ||
|
||
namespace YesWiki\LoginSso; | ||
|
||
use Symfony\Component\HttpFoundation\Request; | ||
use YesWiki\LoginSso\Service\OAuth2ProviderFactory; | ||
use YesWiki\Core\Controller\AuthController; | ||
use YesWiki\Core\Service\TemplateEngine; | ||
use YesWiki\Core\Service\PageManager; | ||
use YesWiki\Core\Service\UserManager; | ||
use YesWiki\Login\Exception\LoginException; | ||
use YesWiki\Core\YesWikiAction; | ||
|
||
class LoginAction extends YesWikiAction | ||
{ | ||
protected AuthController $authController; | ||
|
||
public function run() | ||
{ | ||
$this->authController = $this->getService(AuthController::class); | ||
|
||
$action = $_REQUEST["action"] ?? ''; | ||
switch ($action) { | ||
case "connectOAUTH": | ||
$this->redirectOAuth((int) $_REQUEST["provider"]); | ||
break; | ||
case "logout": | ||
$this->logout(); | ||
default: | ||
return $this->renderDefault(); | ||
} | ||
return null; | ||
} | ||
|
||
private function validateConfig() | ||
{ | ||
// Verification si le fichier de conf est bien renseigné dans toutes les lignes du tableau | ||
$allGood = true; | ||
$error = []; | ||
foreach($this->wiki->config['sso_config']['providers'] as $id => $confEntry) { | ||
if (strtolower($confEntry['auth_type']) == strtolower('oauth2')) { | ||
if ( | ||
empty($confEntry['auth_options']['clientId']) || | ||
empty($confEntry['auth_options']['clientSecret']) || | ||
empty($confEntry['auth_options']['urlAuthorize']) || | ||
empty($confEntry['auth_options']['urlAccessToken']) || | ||
empty($confEntry['auth_options']['urlResourceOwnerDetails']) | ||
) { | ||
$allGood = false; | ||
$error[] = 'Provider No ' . ($id + 1) . ' : ' . _t('SSO_AUTH_OPTIONS_ERROR'); | ||
} | ||
} else { | ||
$allGood = false; | ||
$error[] = 'Provider No ' . ($id + 1) . ' : ' . _t('SSO_AUTH_TYPE_ERROR'); | ||
} | ||
|
||
if (!isset($confEntry['id_sso_field'])) { | ||
$allGood = false; | ||
$error[] = 'Provider No '. ($id + 1) . ' : ' . _t('SSO_USER_ID_REQUIRED'); | ||
} | ||
|
||
if (!isset($confEntry['email_sso_field'])) { | ||
$allGood = false; | ||
$error[] = 'Provider No '. ($id + 1) . ' : ' . _t('SSO_USER_EMAIL_REQUIRED'); | ||
} | ||
} | ||
if (!$allGood) { | ||
throw new \RuntimeException( | ||
_t('action {{login}}') . implode(',', $error), | ||
); | ||
} | ||
} | ||
|
||
private function renderDefault(): string | ||
{ | ||
$this->validateConfig(); | ||
// classe css pour les boutons | ||
$btnclass = $this->wiki->GetParameter("btnclass"); | ||
if (empty($btnclass)) { | ||
$btnclass = 'btn-default'; | ||
} | ||
|
||
$user = $this->authController->getLoggedUser(); | ||
return $this->render('@loginsso/modal.twig', [ | ||
"connected" => !empty($user), | ||
"user" => $user["name"] ?? '', | ||
"email" => $user["email"] ?? '', | ||
"providers" => $this->wiki->config['sso_config']['providers'], | ||
"incomingUrl" => $this->wiki->request->getUri(), | ||
"btnClass" => $btnclass, | ||
"nobtn" => $this->wiki->GetParameter("nobtn") | ||
]); | ||
} | ||
|
||
private function redirectOAuth(int $providerId) | ||
{ | ||
$provider = $this->getService(OAuth2ProviderFactory::class)->createProvider($providerId); | ||
$authorizationUrl = $provider->getAuthorizationUrl(); | ||
|
||
$_SESSION['oauth2state'] = $provider->getState(); | ||
$_SESSION['oauth2previousUrl'] = $this->getIncominUriWithoutAction(); | ||
$_SESSION['oauth2provider'] = $providerId; | ||
|
||
return $this->wiki->Redirect( | ||
$authorizationUrl | ||
); | ||
} | ||
|
||
private function logout() | ||
{ | ||
$this->authController->logout(); | ||
$this->wiki->SetMessage(_t('LOGIN_YOU_ARE_NOW_DISCONNECTED')); | ||
$this->wiki->Redirect($this->getIncominUriWithoutAction()); | ||
$this->wiki->exit(); | ||
} | ||
|
||
/** | ||
* Get current url but remove all extension specific actions | ||
* Used for post authentification redirection | ||
*/ | ||
private function getIncominUriWithoutAction() | ||
{ | ||
parse_str(parse_url($this->wiki->request->getUri(), PHP_URL_QUERY), $query); | ||
unset($query['action']); | ||
unset($query['provider']); | ||
|
||
return $this->wiki->request->getUriForPath($this->wiki->request->getPathInfo() . '?' . http_build_query($query)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,19 +7,22 @@ | |
* @param dash if dash is equal to '1', a dash point will be insered before the link | ||
* | ||
* @category YesWiki | ||
* @package login-sso | ||
* @package loginsso | ||
* @author Adrien Cheype <[email protected]> | ||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL 3.0 | ||
* @link https://yeswiki.net | ||
*/ | ||
namespace YesWiki; | ||
|
||
use function YesWiki\LoginSso\Lib\bazarUserEntryExists; | ||
|
||
|
||
if (!defined("WIKINI_VERSION")) { | ||
die("accès direct interdit"); | ||
} | ||
|
||
// load the login-sso lib | ||
require_once 'tools/login-sso/libs/login-sso.lib.php'; | ||
// load the loginsso lib | ||
require_once 'tools/loginsso/libs/loginsso.lib.php'; | ||
|
||
$user = $this->GetUser(); | ||
|
||
|
Oops, something went wrong.