Skip to content

ISSUE-338: saml login #1064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: release-3.7.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
experimental: true

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up PHP ${{ matrix.php-version }}
uses: shivammathur/setup-php@v2
Expand Down Expand Up @@ -79,8 +79,9 @@ jobs:
cp -fv tests/ci/config.php public_html/lists/config/config.php
mkdir -p output/screenshots
mkdir -p build/mails
chmod -R 777 output/screenshots build/mails
./bin/start-selenium > output/selenium.log 2>&1 &
sleep 5
sleep 15
sudo php -S 0.0.0.0:80 -t public_html > /dev/null 2>&1 &
continue-on-error: ${{ matrix.experimental }}

Expand Down
16 changes: 14 additions & 2 deletions public_html/lists/admin/home.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,20 @@
</p>

<p><span class="total"><?php echo number_format($lastcampaign['total']) ?></span> Messages sent on <span class="total"><?php echo $lastcampaign['sent'] ?></span>.</p>
<p><span class="total"><?php echo number_format($lastcampaign['views']) ?> </span> Viewed (<span class="total"><?php echo sprintf('%0.2f', ($lastcampaign['views'] / ($lastcampaign['total'] - $lastcampaign['bounced']) * 100)) ?>%</span>), and <span class="total"><?php echo $lastcampaign['bounced'] ?></span> bounced (<span class="total"><?php echo sprintf('%0.2f', ($lastcampaign['bounced'] / $lastcampaign['total'] * 100)) ?>%</span>).</p>

<p>
<span class="total"><?php echo number_format($lastcampaign['views']) ?> </span> Viewed
(<?php
echo $lastcampaign['total'] > 0
? '<span class="total">' . sprintf('%0.2f', ($lastcampaign['views'] / ($lastcampaign['total'] - $lastcampaign['bounced']) * 100)) . '%</span>'
: 'N/A';
?>),
and <span class="total"><?php echo $lastcampaign['bounced'] ?></span> bounced
(<?php
echo $lastcampaign['total'] > 0
? '<span class="total">' . sprintf('%0.2f', ($lastcampaign['bounced'] / $lastcampaign['total'] * 100)) . '%</span>'
: 'N/A';
?>).
</p>
<?php
} ?>

Expand Down
9 changes: 9 additions & 0 deletions public_html/lists/admin/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ function mb_strtolower($string)
if (file_exists(dirname(__FILE__).'/../texts/'.$GLOBALS['language_module'])) {
include_once dirname(__FILE__).'/../texts/'.$GLOBALS['language_module'];
}
@session_start();
include_once dirname(__FILE__).'/languages.php';
require_once dirname(__FILE__).'/defaultconfig.php';

Expand Down Expand Up @@ -290,6 +291,11 @@ function mb_strtolower($string)
}
echo "$page_title</title>";
$inRemoteCall = false;
if ($page_title === 'enablelogin') {
SaveConfig('hide_default_login', 0);
header('Location: ?page=home');
exit;
}
$doLoginCheck = Sql_Table_exists($tables['admin_login']);

if (!empty($GLOBALS['require_login'])) {
Expand Down Expand Up @@ -374,6 +380,9 @@ function mb_strtolower($string)
//$msg = 'Not logged in';
$logged = false;
foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
if ($pluginname == 'simplesaml' && !isset($_GET[$GLOBALS['plugins'][$pluginname]->autUrl])) {
continue;
}
if ($plugin->login()) {
$logged = true;
break;
Expand Down
6 changes: 5 additions & 1 deletion public_html/lists/admin/init.php
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,11 @@
if (!isset($allowed_referrers) || !is_array($allowed_referrers)) {
$allowed_referrers = array();
}
if (isset($_SERVER['HTTP_ORIGIN']) && defined('ACCESS_CONTROL_ALLOW_ORIGINS') && in_array($_SERVER['HTTP_ORIGIN'], ACCESS_CONTROL_ALLOW_ORIGINS)) {
if (
isset($_SERVER['HTTP_ORIGIN'])
&& defined('ACCESS_CONTROL_ALLOW_ORIGINS')
&& in_array($_SERVER['HTTP_ORIGIN'], ACCESS_CONTROL_ALLOW_ORIGINS)
) {
define('ACCESS_CONTROL_ALLOW_ORIGIN', $_SERVER['HTTP_ORIGIN']);
} elseif (!defined('ACCESS_CONTROL_ALLOW_ORIGIN')) {
define('ACCESS_CONTROL_ALLOW_ORIGIN', $GLOBALS['scheme'].'://'.$_SERVER['HTTP_HOST']);
Expand Down
1 change: 0 additions & 1 deletion public_html/lists/admin/languages.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ function lanSort($a, $b)
if (!empty($GLOBALS['SessionTableName'])) {
require_once dirname(__FILE__).'/sessionlib.php';
}
@session_start();

if (isset($_POST['setlanguage']) && !empty($_POST['setlanguage']) && is_array($LANGUAGES[$_POST['setlanguage']])) {
//# just in case
Expand Down
65 changes: 54 additions & 11 deletions public_html/lists/admin/login.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,41 @@ function footer()
echo '</div></form>';
}

function enableDefaultLogin()
{
echo '<div class="login"><p>';
echo '<strong>' . $GLOBALS['I18N']->get('Having trouble with SSO login?') . '</strong><br>';
echo $GLOBALS['I18N']->get('You can still use default login by clicking on the button') . ' ';
echo '<a href="?page=enablelogin" class="submit">' . $GLOBALS['I18N']->get('Enable default login') . '</a>';
echo '</p><div class="clear"></div></div>';
}


function renderSSO()
{
if (!empty($GLOBALS['ssoplugin'])) {
echo '<form method="post" id="forgotpassword-form" action="">';
echo '<div style="display: flex; justify-content: space-around; align-items: center;">';

foreach ($GLOBALS['ssoplugin'] as $plugin) {
if (isset($GLOBALS['plugins'][$plugin])) {
$pluginInstance = $GLOBALS['plugins'][$plugin];
$ssoUrl = $pluginInstance->autUrl;
$buttonText = 'Login with ' . getConfig($pluginInstance->name);

echo '<a href="?' . $ssoUrl . '"
style="display: inline-block; padding: 8px 15px; background-color: #3c3c3c; color: #fff;
text-decoration: none; border-radius: 5px; font-size: 16px; text-align: center;
min-width: 120px;">
' . $buttonText . '
</a>';
}
}

echo '</div>';
echo '</form>';
}
}
//Delete from the DB every token older than certain elapsed time.
function deleteOldTokens()
{
Expand Down Expand Up @@ -116,16 +151,24 @@ function deleteOldTokens()
exit;
}
} else {
echo "<form method=\"post\" id=\"login-form\" action=\"\">\n";
echo " <input type=\"hidden\" name=\"page\" value=\"$page\" />\n";
echo " <table class=\"loginPassUpdate\" width=\"100%\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\">\n";
echo ' <tr><td><span class="general">'.$GLOBALS['I18N']->get('Name').":</span></td></tr>\n";
echo ' <tr><td><input type="text" name="login" value="" size="30" autofocus="autofocus" /></td></tr>';
echo ' <tr><td><span class="general">'.$GLOBALS['I18N']->get('Password').':</span></td></tr>';
echo ' <tr><td><input type="password" name="password" value="" size="30" /></td></tr>';
echo ' <tr><td><input class="submit" type="submit" name="process" value="'.$GLOBALS['I18N']->get('Continue').'" /></td></tr>';
echo ' </table>';
echo '</form>';
footer();
$showDefaultLogin = !isset($GLOBALS['ssoplugin']) || !getConfig('hide_default_login');
if ($showDefaultLogin) {
echo "<form method=\"post\" id=\"login-form\" action=\"\">\n";
echo " <input type=\"hidden\" name=\"page\" value=\"$page\" />\n";
echo " <table class=\"loginPassUpdate\" width=\"100%\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\">\n";
echo ' <tr><td><span class="general">'.$GLOBALS['I18N']->get('Name').":</span></td></tr>\n";
echo ' <tr><td><input type="text" name="login" value="" size="30" autofocus="autofocus" /></td></tr>';
echo ' <tr><td><span class="general">'.$GLOBALS['I18N']->get('Password').':</span></td></tr>';
echo ' <tr><td><input type="password" name="password" value="" size="30" /></td></tr>';
echo ' <tr><td><input class="submit" type="submit" name="process" value="'.$GLOBALS['I18N']->get('Continue').'" /></td></tr>';
echo ' </table>';
echo '</form>';
footer();
}
renderSSO();

if (!$showDefaultLogin) {
enableDefaultLogin();
}
}
?>
5 changes: 5 additions & 0 deletions public_html/lists/admin/pluginlib.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
$GLOBALS['emailsenderplugin'] = false;
$GLOBALS['analyticsqueryplugin'] = false;
$GLOBALS['updaterplugin'] = false;
$GLOBALS['ssoplugin'] = [];

$pluginRootDirs = array();
if (PLUGIN_ROOTDIRS != '') {
Expand Down Expand Up @@ -111,6 +112,10 @@
) {
$GLOBALS['editorplugin'] = $className;
}
if (method_exists($pluginInstance, 'login') && isset($pluginInstance->ssoProvider))
{
$GLOBALS['ssoplugin'][] = $className;
}
if (!$GLOBALS['authenticationplugin'] && $pluginInstance->authProvider && method_exists($pluginInstance,
'validateLogin')
) {
Expand Down
2 changes: 1 addition & 1 deletion public_html/lists/admin/plugins/.htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
Require all denied
</IfModule>
</FilesMatch>
<FilesMatch "index.php$">
<FilesMatch "(index.php|module.php)$">
# Apache < 2.3
<IfModule !mod_authz_core.c>
Order allow,deny
Expand Down
13 changes: 8 additions & 5 deletions tests/ci/behat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ default:
- ./features
contexts:
- FeatureContext:
# Set database access credentials
database:
host: 127.0.0.1
user: phplist
password: phplist
name: phplistdb
# Set admin user login credentials
admin:
username: admin
password: Mypassword123+
Expand All @@ -24,7 +22,7 @@ default:
- FailAid\Context\FailureContext
extensions:
Behat\MinkExtension:
show_cmd: 'cp %s ./output/lastResponse.html' #'cat %s'
show_cmd: 'cp %s ./output/lastResponse.html'
base_url: 'http://127.0.0.1'
default_session: chrome
sessions:
Expand All @@ -35,6 +33,11 @@ default:
browserName: chrome
browser: chrome
version: ""
extra_capabilities:
timeouts:
implicit: 10000 # 5 seconds for implicit waits
pageLoad: 60000 # 30 seconds for page loads
script: 60000 # 30 seconds for async scripts
chrome:
switches:
- "--headless"
Expand All @@ -54,7 +57,7 @@ default:
selenium2:
browser: "firefox"
wd_host: http://127.0.0.1:4444/wd/hub
FailAid\Extension:
FailAid\Extension:
screenshot:
directory: ./output/screenshots/
mode: default
Expand All @@ -65,7 +68,7 @@ default:
image_drivers:
local:
screenshot_directory: output/screenshots
clear_screenshot_directory: true
clear_screenshot_directory: true

chrome:
extensions:
Expand Down
37 changes: 26 additions & 11 deletions tests/features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
<?php

use Behat\Behat\Context\Context;

use Behat\Mink\Exception\ExpectationException;
use Behat\MinkExtension\Context\MinkContext;
#use Behat\MinkExtension\Context\RawMinkContext;

//
// Require 3rd-party libraries here:
//
// require_once 'PHPUnit/Autoload.php';
// require_once 'PHPUnit/Framework/Assert/Functions.php';
//

/**
* Features context.
Expand Down Expand Up @@ -107,7 +98,7 @@ public function spins($closure, $tries = 10)
$closure();

return;
} catch (\Exception $e) {
} catch (Exception $e) {
if ($i == $tries) {
throw $e;
}
Expand Down Expand Up @@ -179,7 +170,7 @@ public function isLoggedIn($throwsException = false)
{
$retVal = $this->token != null;
if(!$retVal && $throwsException){
throw new \Exception('Not logged in yet');
throw new Exception('Not logged in yet');
}
return $retVal;
}
Expand Down Expand Up @@ -325,4 +316,28 @@ public function iConfirmThePopup()
$this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
}

/**
* @Then I must see :text
*/
public function iMustSee($text)
{
$maxAttempts = 3;
$waitTimeMs = 1000;
$this->getSession()->wait(5000, "document.readyState === 'complete'");

for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
try {
$this->assertSession()->pageTextContains($this->fixStepArgument($text));
return;
} catch (\WebDriver\Exception\StaleElementReference $e) {
// Handle Selenium stale element exception, retry
} catch (\Behat\Mink\Exception\ResponseTextException $e) {
// Handle Mink text assertion failure, retry
}

usleep($waitTimeMs * 1000);
}

throw new Exception(sprintf("Text '%s' not found after %d attempts.", $text, $maxAttempts));
}
}
6 changes: 2 additions & 4 deletions tests/features/bootstrap/SubscriberContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
require __DIR__.'/bootstrap.php';

use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\TableNode;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;

Expand All @@ -25,10 +24,9 @@ public function beforeScenario(BeforeScenarioScope $scope)

/**
* @param string $list
* @param array $table
*
* @Given /I have "(.*)" list with the following subscribers:/
* @param PyStringNode $stringNode
* @throws Exception
* @Given /I have "(.*)" list with the following subscribers:/
*/
public function iHaveSubscribersForList($list, PyStringNode $stringNode)
{
Expand Down
4 changes: 2 additions & 2 deletions tests/features/getting-started/login.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ Feature: Login
When I fill in "login" with a valid username
And I fill in "password" with a valid password
And I press "Continue"
Then I should see "Start or continue a campaign"
Then I must see "Start or continue a campaign"

Scenario: Login with bad credentials
Given I am on "/lists/admin/"
When I fill in "login" with "no-user"
And I fill in "password" with "no-password"
And I press "Continue"
Then I should see "Incorrect password"
Then I must see "Incorrect password"

# Scenario: Login with only a username
# Given I am on "/lists/admin/"
Expand Down
2 changes: 1 addition & 1 deletion tests/features/getting-started/menu.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Feature: Navigate the app using the menu

Scenario Outline: Use main menu navigation links
Given I have logged in as an administrator
Then I should see "<Pagename>"
Then I must see "<Pagename>"
Examples:
| Pagename |
| dashboard |
Expand Down
Loading
Loading