Skip to content
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
74 changes: 56 additions & 18 deletions lib/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,50 @@ public function processLogin($username, $password, $addToHistory = true)
{
$db = DatabaseConnection::getInstance();

if (isset($_SERVER['REMOTE_ADDR']))
{
$ip = $_SERVER['REMOTE_ADDR'];
}
else
{
$ip = '';
}

if (isset($_SERVER['HTTP_USER_AGENT']))
{
$userAgent = $_SERVER['HTTP_USER_AGENT'];
}
else
{
$userAgent = '';
}

if (!empty($ip))
{
$sql = sprintf(
"SELECT
COUNT(*) AS failedAttempts
FROM
user_login
WHERE
ip = %s
AND
successful = 0
AND
date >= (NOW() - INTERVAL 10 MINUTE)",
$db->makeQueryString($ip)
);

$rs = $db->getAssoc($sql);
if (!empty($rs) && $rs['failedAttempts'] > 10)
{
$this->_isLoggedIn = false;
$this->_loginError = 'Too many failed login attempts. Please try again later.';

return;
}
}

/* Is the login information supplied correct? Get the status flag. */
$users = new Users(-1);
$loginStatus = $users->isCorrectLogin($username, $password);
Expand All @@ -676,6 +720,18 @@ public function processLogin($username, $password, $addToHistory = true)
$this->_isLoggedIn = false;
$this->_loginError = 'Invalid username or password.';

/* Log the login as unsuccessful. */
if ($addToHistory)
{
$users->addLoginHistory(
null,
0,
$ip,
$userAgent,
false
);
}

return;
}

Expand Down Expand Up @@ -728,24 +784,6 @@ public function processLogin($username, $password, $addToHistory = true)
return;
}

if (isset($_SERVER['REMOTE_ADDR']))
{
$ip = $_SERVER['REMOTE_ADDR'];
}
else
{
$ip = '';
}

if (isset($_SERVER['HTTP_USER_AGENT']))
{
$userAgent = $_SERVER['HTTP_USER_AGENT'];
}
else
{
$userAgent = '';
}

switch ($loginStatus)
{
case LOGIN_INVALID_PASSWORD:
Expand Down
18 changes: 16 additions & 2 deletions lib/Users.php
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,20 @@ public function usernameExists($username)
public function addLoginHistory($userID, $siteID, $ip, $userAgent,
$wasSuccessful)
{
if ($userID === null || $userID === '')
{
$userIDSQL = 'NULL';
}
else
{
$userIDSQL = $this->_db->makeQueryInteger($userID);
}

if ($siteID === null || $siteID === '')
{
$siteID = 0;
}

if (ENABLE_HOSTNAME_LOOKUP)
{
$hostname = @gethostbyaddr($ip);
Expand Down Expand Up @@ -986,8 +1000,8 @@ public function addLoginHistory($userID, $siteID, $ip, $userAgent,
%s,
NOW()
)",
$userID,
$siteID,
$userIDSQL,
$this->_db->makeQueryInteger($siteID),
$this->_db->makeQueryString($ip),
$this->_db->makeQueryString($userAgent),
$this->_db->makeQueryString($hostname),
Expand Down
22 changes: 11 additions & 11 deletions test/features/GET_POST_requestsSecurity.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Feature: Security using ACL - actions - GET & POST
In order to protect sensitive information from users who shouldd not have access to them
All accesses in the system need to be controlled by the Access Control List

@candidates @actions
@candidates @actions @reset_login_attempts
Scenario Outline: Candidate module actions
Given I am logged in with <accessLevel> access level

Expand Down Expand Up @@ -223,7 +223,7 @@ Scenario Outline: Candidate module actions



@joborders @actions
@joborders @actions @reset_login_attempts
Scenario Outline: Job Order module actions
Given I am logged in with <accessLevel> access level

Expand Down Expand Up @@ -411,7 +411,7 @@ Scenario Outline: Job Order module actions



@companies @actions
@companies @actions @reset_login_attempts
Scenario Outline: Companies module actions
Given I am logged in with <accessLevel> access level

Expand Down Expand Up @@ -526,7 +526,7 @@ Examples:
| ROOT | POST | index.php?m=companies&a=createAttachment | |


@contacts @actions
@contacts @actions @reset_login_attempts
Scenario Outline: Contacts module actions
Given I am logged in with <accessLevel> access level

Expand Down Expand Up @@ -640,7 +640,7 @@ Scenario Outline: Contacts module actions
| ROOT | POST | index.php?m=contacts&a=edit | |
| ROOT | POST | index.php?m=contacts&a=addActivityScheduleEvent | |

@activities @actions
@activities @actions @reset_login_attempts
Scenario Outline: Activity module actions
Given I am logged in with <accessLevel> access level

Expand Down Expand Up @@ -674,7 +674,7 @@ Scenario Outline: Activity module actions
| ROOT | GET | index.php?m=activity&a=listByViewDataGrid | |
| ROOT | POST | index.php?m=activity&a=viewByDate | |

@dashboard @home @actions
@dashboard @home @actions @reset_login_attempts
Scenario Outline: Home module actions
Given I am logged in with <accessLevel> access level

Expand Down Expand Up @@ -724,7 +724,7 @@ Scenario Outline: Home module actions
| ROOT | GET | index.php?m=home&a=getAttachment | |
| ROOT | GET | index.php?m=home&a=home | |

@lists @actions
@lists @actions @reset_login_attempts
Scenario Outline: Lists module actions
Given I am logged in with <accessLevel> access level

Expand Down Expand Up @@ -783,7 +783,7 @@ Scenario Outline: Lists module actions
| ROOT | GET | index.php?m=lists&a=listByView | |


@calendar @actions
@calendar @actions @reset_login_attempts
Scenario Outline: Calendar module actions
Given I am logged in with <accessLevel> access level

Expand Down Expand Up @@ -833,7 +833,7 @@ Scenario Outline: Calendar module actions
| ROOT | POST | index.php?m=calendar&a=addEvent | |
| ROOT | POST | index.php?m=calendar&a=editEvent | |

@reports @actions
@reports @actions @reset_login_attempts
Scenario Outline: Reports module actions
Given I am logged in with <accessLevel> access level

Expand Down Expand Up @@ -907,7 +907,7 @@ Scenario Outline: Reports module actions
| ROOT | GET | index.php?m=reports&a=generateEEOReportPreview | |
| ROOT | GET | index.php?m=reports&a=reports | |

@settings @actions
@settings @actions @reset_login_attempts
Scenario Outline: Settings module actions
Given I am logged in with <accessLevel> access level

Expand Down Expand Up @@ -1369,4 +1369,4 @@ Scenario Outline: Reports module actions

#When I do GET request "index.php?m=settings&a=ajax_wizardWebsite"
#And the response should <FWebsite> contain "You don't have permission"


34 changes: 34 additions & 0 deletions test/features/bootstrap/SecurityContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,40 @@ public function __construct()
{
}

/**
* Test-only: Some security scenarios intentionally perform many failed login attempts (e.g. disabled users).
* This can accumulate rows in `user_login` and trigger the IP-based brute-force lockout, which then breaks
* subsequent login steps within the same CI run. For scenarios tagged with @reset_login_attempts, reset the
* login attempt history to keep tests isolated from this side effect.
*
* @BeforeScenario
*/
public function resetLoginAttemptsForScenario($event)
{
$scenario = $event->getScenario();
if (method_exists($scenario, 'hasTag'))
{
$hasTag = $scenario->hasTag('reset_login_attempts');
}
else
{
$tags = $scenario->getTags();
$hasTag = in_array('reset_login_attempts', $tags, true);
}

if (!$hasTag)
{
return;
}

$db = DatabaseConnection::getInstance();
$result = $db->query('TRUNCATE TABLE user_login');
if ($result === false)
{
throw new Exception('Failed to truncate user_login for scenario: ' . $scenario->getTitle());
}
}

/**
* @Given I am logged in with :accessLevel access level
*/
Expand Down
20 changes: 10 additions & 10 deletions test/features/moduleMainPagesSecurity.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Feature: Access Level to objects check - main pages

######## DASHBOARD(HOME) #######

@javascript @dashboard
@javascript @dashboard @reset_login_attempts
Scenario Outline: Dashboard module visibility
Given I am logged in with <accessLevel> access level
And I am on "/index.php?m=home"
Expand All @@ -32,7 +32,7 @@ Feature: Access Level to objects check - main pages

####### ACTIVITIES #######

@javascript @activities
@javascript @activities @reset_login_attempts
Scenario Outline: Activities module visibility
Given I am logged in with <accessLevel> access level
And I am on "/index.php?m=activity"
Expand Down Expand Up @@ -64,7 +64,7 @@ Feature: Access Level to objects check - main pages

####### JOB ORDERS #######

@javascript @joborders
@javascript @joborders @reset_login_attempts
Scenario Outline: Job Orders module visibility
Given I am logged in with <accessLevel> access level
And I am on "/index.php?m=joborders"
Expand Down Expand Up @@ -98,7 +98,7 @@ Feature: Access Level to objects check - main pages

####### CANDIDATES #######

@javascript @candidates
@javascript @candidates @reset_login_attempts
Scenario Outline: Candidates module visibility
Given I am logged in with <accessLevel> access level
And I am on "/index.php?m=candidates"
Expand Down Expand Up @@ -134,7 +134,7 @@ Feature: Access Level to objects check - main pages

####### COMPANIES #######

@javascript @companies
@javascript @companies @reset_login_attempts
Scenario Outline: Companies module visibility
Given I am logged in with <accessLevel> access level
And I am on "/index.php?m=companies"
Expand Down Expand Up @@ -167,7 +167,7 @@ Feature: Access Level to objects check - main pages

####### CONTACTS #######

@javascript @contacts
@javascript @contacts @reset_login_attempts
Scenario Outline: Contacts module visibility
Given I am logged in with <accessLevel> access level
And I am on "/index.php?m=contacts"
Expand Down Expand Up @@ -201,7 +201,7 @@ Feature: Access Level to objects check - main pages

####### LISTS #######

@javascript @lists
@javascript @lists @reset_login_attempts
Scenario Outline: Lists module visibility
Given I am logged in with <accessLevel> access level
And I am on "/index.php?m=lists"
Expand All @@ -227,7 +227,7 @@ Feature: Access Level to objects check - main pages

####### REPORTS #######

@javascript @reports
@javascript @reports @reset_login_attempts
Scenario Outline: Reports module visibility
Given I am logged in with <accessLevel> access level
And I am on "/index.php?m=reports"
Expand Down Expand Up @@ -257,7 +257,7 @@ Feature: Access Level to objects check - main pages

####### SETTINGS #######

@javascript @settings
@javascript @settings @reset_login_attempts
Scenario Outline: Settings module visibility
Given I am logged in with <accessLevel> access level
And I am on "/index.php?m=settings"
Expand All @@ -283,7 +283,7 @@ Feature: Access Level to objects check - main pages

####### CALENDAR #######

@javascript @calendar
@javascript @calendar @reset_login_attempts
Scenario Outline: Calendar module visibility
Given I am logged in with <accessLevel> access level
And I am on "/index.php?m=calendar"
Expand Down
Loading
Loading