396 lines
12 KiB
PHP
Executable File
396 lines
12 KiB
PHP
Executable File
<?php
|
|
|
|
/**
|
|
* Get the configured path to the root of the SimpleSAML library
|
|
* If $simplesaml_lib_path is not set this will be the [webroot]/plugins/simplesaml/lib folder
|
|
*
|
|
* @return string
|
|
*/
|
|
function simplesaml_get_lib_path()
|
|
{
|
|
global $simplesaml_lib_path, $simplesaml_rsconfig;
|
|
|
|
$lib_path = __DIR__ . '/../lib';
|
|
|
|
if ('' == $simplesaml_lib_path || $simplesaml_rsconfig) {
|
|
return $lib_path;
|
|
}
|
|
|
|
$lib_path2 = $simplesaml_lib_path;
|
|
|
|
if ('/' == substr($lib_path2, -1)) {
|
|
$lib_path2 = rtrim($lib_path2, '/');
|
|
}
|
|
|
|
if (file_exists($lib_path2)) {
|
|
$lib_path = $lib_path2;
|
|
}
|
|
|
|
return $lib_path;
|
|
}
|
|
|
|
/**
|
|
* Authenticate user, redfirecting to IdP if necessary
|
|
*
|
|
* @return boolean
|
|
*/
|
|
function simplesaml_authenticate()
|
|
{
|
|
global $as;
|
|
if (!simplesaml_is_configured()) {
|
|
debug("simplesaml: plugin not configured.");
|
|
return false;
|
|
}
|
|
if (!isset($as)) {
|
|
require_once simplesaml_get_lib_path() . '/lib/_autoload.php';
|
|
$spname = get_saml_sp_name();
|
|
debug("simplesaml: Using SP name '{$spname}'");
|
|
$as = new SimpleSAML\Auth\Simple($spname);
|
|
}
|
|
$as->requireAuth();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get SAML attributes
|
|
*
|
|
* @return array
|
|
*/
|
|
function simplesaml_getattributes()
|
|
{
|
|
global $as;
|
|
if (!isset($as)) {
|
|
require_once simplesaml_get_lib_path() . '/lib/_autoload.php';
|
|
$spname = get_saml_sp_name();
|
|
$as = new SimpleSAML\Auth\Simple($spname);
|
|
}
|
|
$as->requireAuth();
|
|
return $as->getAttributes();
|
|
}
|
|
|
|
/**
|
|
* Sign out of SAML SP
|
|
*
|
|
* @return void
|
|
*/
|
|
function simplesaml_signout()
|
|
{
|
|
global $baseurl, $as;
|
|
if (!simplesaml_is_configured()) {
|
|
debug("simplesaml: plugin not configured.");
|
|
return false;
|
|
}
|
|
if (!isset($as)) {
|
|
require_once simplesaml_get_lib_path() . '/lib/_autoload.php';
|
|
$spname = get_saml_sp_name();
|
|
$as = new SimpleSAML\Auth\Simple($spname);
|
|
}
|
|
if ($as->isAuthenticated()) {
|
|
$as->logout($baseurl . "/login.php");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if user has been authenticated by SimpleSAMLPHP
|
|
*
|
|
* @return boolean
|
|
*/
|
|
function simplesaml_is_authenticated()
|
|
{
|
|
global $as,$simplesaml_authenticated;
|
|
if (!simplesaml_is_configured()) {
|
|
debug("simplesaml: plugin not configured.");
|
|
return false;
|
|
}
|
|
|
|
if (isset($simplesaml_authenticated)) {
|
|
return $simplesaml_authenticated;
|
|
}
|
|
|
|
if (!isset($as)) {
|
|
require_once simplesaml_get_lib_path() . '/lib/_autoload.php';
|
|
$spname = get_saml_sp_name();
|
|
$as = new SimpleSAML\Auth\Simple($spname);
|
|
}
|
|
if (isset($as) && $as->isAuthenticated()) {
|
|
$simplesaml_authenticated = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function simplesaml_getauthdata($value)
|
|
{
|
|
if (!simplesaml_is_configured()) {
|
|
debug("simplesaml: plugin not configured.");
|
|
return false;
|
|
}
|
|
global $as;
|
|
if (!isset($as)) {
|
|
require_once simplesaml_get_lib_path() . '/lib/_autoload.php';
|
|
$spname = get_saml_sp_name();
|
|
$as = new SimpleSAML\Auth\Simple($spname);
|
|
}
|
|
$as->requireAuth();
|
|
return $as->getAuthData($value)->getValue();
|
|
}
|
|
|
|
/**
|
|
* Notify of a new SAML user with an email address that is already in use by an existing user
|
|
*
|
|
* @param string $username Username
|
|
* @param int $group Usergroup
|
|
* @param string $email Email
|
|
* @param array $email_matches Array of existing users with matching email
|
|
* @param int $newuserid ID of new user if created
|
|
* @return void
|
|
*/
|
|
function simplesaml_duplicate_notify($username, $group, $email, $email_matches, $newuserid = 0)
|
|
{
|
|
global $lang, $baseurl, $baseurl_short, $simplesaml_multiple_email_notify, $user_pref_user_management_notifications,
|
|
$email_user_notifications, $applicationname;
|
|
debug("simplesaml - user authenticated with matching email for existing users: " . $email);
|
|
$message = $lang['simplesaml_multiple_email_match_text'] . " " . $email . "<br /><br />";
|
|
$messageurl = "";
|
|
if ($newuserid > 0) {
|
|
$messageurl = generateURL("{$baseurl}/", ['u' => $newuserid]);
|
|
}
|
|
|
|
$message .= "</a><table class=\"InfoTable\" style=\"width:100%\"border=1>";
|
|
$message .= sprintf(
|
|
'<tr><th>%s</th><th>%s</th><th>%s</th></tr>',
|
|
escape($lang['property-name']),
|
|
escape($lang['property-reference']),
|
|
escape($lang['username'])
|
|
);
|
|
foreach ($email_matches as $email_match) {
|
|
$message .= sprintf(
|
|
'<tr>
|
|
<td><a href="%1$s" target="_blank">%2$s</a></td>
|
|
<td><a href="%1$s" target="_blank">%3$s</a></td>
|
|
<td><a href="%1$s" target="_blank">%4$s</a></td>
|
|
</tr>\n',
|
|
generateURL("{$baseurl}/", ['u' => $email_match["ref"]]),
|
|
escape($email_match['fullname']),
|
|
escape($email_match['ref']),
|
|
escape($email_match['username'])
|
|
);
|
|
}
|
|
|
|
$message .= "</table><a>";
|
|
$emailmessage = $message;
|
|
if ($messageurl != "") {
|
|
$emailmessage .= sprintf(
|
|
'%s: <a href="%s">%s</a><br />',
|
|
escape($lang['simplesaml_usercreated']),
|
|
$messageurl,
|
|
escape($username)
|
|
);
|
|
}
|
|
|
|
$notify_users = ps_query("SELECT ref, email, usergroup FROM user WHERE email = ?", array("s", $simplesaml_multiple_email_notify));
|
|
$message_users = array();
|
|
foreach ($notify_users as $notify_user) {
|
|
get_config_option(
|
|
['user' => $notify_user['ref'], 'usergroup' => $notify_user['usergroup']],
|
|
'user_pref_user_management_notifications',
|
|
$send_message,
|
|
$user_pref_user_management_notifications
|
|
);
|
|
if (!$send_message) {
|
|
continue;
|
|
}
|
|
|
|
get_config_option(['user' => $notify_user['ref'], 'usergroup' => $notify_user['usergroup']], 'email_user_notifications', $send_email, $email_user_notifications);
|
|
if ($send_email && filter_var($notify_user["email"], FILTER_VALIDATE_EMAIL)) {
|
|
send_mail(
|
|
$notify_user['email'],
|
|
"{$applicationname}: {$lang['simplesaml_multiple_email_match_subject']}",
|
|
$emailmessage
|
|
);
|
|
} else {
|
|
$message_users[] = $notify_user["ref"];
|
|
}
|
|
}
|
|
if (count($message_users) > 0) {
|
|
// Send a message with long timeout (30 days)
|
|
message_add($message_users, str_replace($baseurl . "/", $baseurl_short, $message), $messageurl);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check that the SimpleSAMLphp configuration is valid
|
|
*
|
|
* @return boolean
|
|
*/
|
|
function simplesaml_config_check()
|
|
{
|
|
global $simplesaml_version, $lang;
|
|
|
|
if (!simplesaml_is_configured()) {
|
|
debug("simplesaml: plugin not configured.");
|
|
return false;
|
|
}
|
|
require_once simplesaml_get_lib_path() . '/lib/_autoload.php';
|
|
$config = \SimpleSAML\Configuration::getInstance();
|
|
$version = $config->getVersion();
|
|
|
|
if ($version != $simplesaml_version) {
|
|
if (get_sysvar("SAML_UPGRADE_REQUIRED", 0) != 1) {
|
|
system_notification(
|
|
$lang['simplesaml_authorisation_version_error'],
|
|
"https://www.resourcespace.com/knowledge-base/plugins/simplesaml#saml_instructions_migrate"
|
|
);
|
|
// Set flag so this is not sent multiple times
|
|
set_sysvar("SAML_UPGRADE_REQUIRED", 1);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function simplesaml_php_check()
|
|
{
|
|
global $simplesaml_check_phpversion,$simplesaml_php_check;
|
|
|
|
// Check whether PHP version will cause an error with current SAML config
|
|
if (!isset($simplesaml_php_check)) {
|
|
// Check if not already checked previously
|
|
$simplesaml_php_check = (
|
|
simplesaml_config_check()
|
|
|| version_compare(phpversion(), $simplesaml_check_phpversion, '<')
|
|
);
|
|
}
|
|
return $simplesaml_php_check;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check that the SimpleSAMLphp has been configured.
|
|
* This is done by either:-
|
|
* a) Adding config, authsources and metadata files manually to the configured lib folder ($simplesaml_lib_path) or
|
|
* b) By setting the options in the $simplesamlconfig variable and then enabling the
|
|
* plugin option 'Use ResourceSpace configuration to set SP configuration and metadata'
|
|
*
|
|
* @return boolean
|
|
*/
|
|
function simplesaml_is_configured()
|
|
{
|
|
global $simplesamlconfig, $simplesaml_rsconfig;
|
|
if (
|
|
($simplesaml_rsconfig && !isset($simplesamlconfig))
|
|
||
|
|
(!$simplesaml_rsconfig && !(file_exists(simplesaml_get_lib_path() . '/config/config.php')))
|
|
) {
|
|
debug("simplesaml: plugin not configured.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate a key/certificate pair
|
|
*
|
|
* @param array $dn Array of certificate attributes with named indexes as below
|
|
* - "countryName"
|
|
* - "stateOrProvinceName"
|
|
* - "localityName"
|
|
* - "organizationName"
|
|
* - "commonName"
|
|
* - "emailAddress"
|
|
*
|
|
*
|
|
* @return array Array containing paths to private key (.pem) and certificate (.crt) files
|
|
*/
|
|
function simplesaml_generate_keypair($dn)
|
|
{
|
|
global $storagedir;
|
|
// Generate key pair
|
|
$privkey = openssl_pkey_new(array(
|
|
"private_key_bits" => 4096,
|
|
"private_key_type" => OPENSSL_KEYTYPE_RSA,
|
|
));
|
|
|
|
// Generate CSR and certificate
|
|
$csr = openssl_csr_new($dn, $privkey, array('digest_alg' => 'AES-128-CBC'));
|
|
$x509 = openssl_csr_sign($csr, null, $privkey, 3650, array('digest_alg' => 'AES-128-CBC'));
|
|
|
|
// Save key and certificate
|
|
$pempath = $storagedir . "/system/" . uniqid("saml_") . ".pem";
|
|
$crtpath = $storagedir . "/system/" . uniqid("saml_") . ".crt";
|
|
openssl_x509_export_to_file($x509, $crtpath);
|
|
openssl_pkey_export_to_file($privkey, $pempath);
|
|
|
|
return array(
|
|
'privatekey' => $pempath,
|
|
'certificate' => $crtpath
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the name of the saml sp to use
|
|
*
|
|
* @return string
|
|
*/
|
|
function get_saml_sp_name()
|
|
{
|
|
global $simplesaml_sp, $safe_sp, $simplesaml_rsconfig, $simplesamlconfig;
|
|
if ($safe_sp != "") {
|
|
return $safe_sp;
|
|
}
|
|
|
|
$default_sp_name = "resourcespace-sp";
|
|
$safe_sp = "";
|
|
if (
|
|
!$simplesaml_rsconfig
|
|
|| (isset($simplesamlconfig["authsources"]) && is_array($simplesamlconfig["authsources"]))
|
|
) {
|
|
// If SAML has been configured we need to ensure that defined SP is valid
|
|
$use_error_exception_cache = $GLOBALS["use_error_exception"] ?? false;
|
|
$GLOBALS["use_error_exception"] = true;
|
|
try {
|
|
$as = new SimpleSAML\Auth\Simple($simplesaml_sp);
|
|
$as->getAuthSource();
|
|
} catch (exception $e) {
|
|
// Invalid SP name, use default
|
|
$simplesaml_sp = $default_sp_name;
|
|
}
|
|
$GLOBALS["use_error_exception"] = $use_error_exception_cache;
|
|
} else {
|
|
$simplesaml_sp = $default_sp_name;
|
|
}
|
|
return $simplesaml_sp;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the latest expiration date for the given SAML Identity Provider's certificates
|
|
*
|
|
* @param string $entityid EntityID of SAML IdP
|
|
*
|
|
* @return string Expiration date. Empty string if no certificate or IdP is not found.
|
|
*
|
|
*/
|
|
function get_saml_metadata_expiry($entityid): string
|
|
{
|
|
$expiry = "";
|
|
if (!isset($GLOBALS["simplesamlconfig"]["metadata"][$entityid])) {
|
|
return $expiry;
|
|
} else {
|
|
if (isset($GLOBALS["simplesamlconfig"]["metadata"][$entityid]["keys"])) {
|
|
// Each IdP may have multiple certificates
|
|
foreach ($GLOBALS["simplesamlconfig"]["metadata"][$entityid]["keys"] as $idpkey) {
|
|
if ($idpkey["type"] == 'X509Certificate') {
|
|
$keyexpiry = getCertificateExpiry(preg_replace('/\s+/', '', $idpkey["X509Certificate"]));
|
|
if ($expiry == "" || $keyexpiry > $expiry) {
|
|
$expiry = $keyexpiry;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $expiry;
|
|
}
|
|
}
|