Periodic merge upstream #5
@@ -38,10 +38,11 @@ function authenticateLDAP (string $username, string $password) {
 | 
			
		||||
			$commonName = ldap_get_entries($ds, $ldapSearchResults)[0]['cn'][0];
 | 
			
		||||
			// Create JWT-payload
 | 
			
		||||
			$jwtPayload = [
 | 
			
		||||
				'iat'	=> time(),					// Issued at: time when the token was generated
 | 
			
		||||
				'iss'	=> $_SERVER['SERVER_NAME'],	// Issuer
 | 
			
		||||
				'sub'	=> $qualifiedUsername,		// Subject (ie. username)
 | 
			
		||||
				'name'	=> $commonName				// Common name (as retrieved from AD)
 | 
			
		||||
				'iat'	=> time(),                                              // Issued at: time when the token was generated
 | 
			
		||||
				'iss'	=> $_SERVER['SERVER_NAME'],                             // Issuer
 | 
			
		||||
				'sub'	=> $qualifiedUsername,                                  // Subject (ie. username)
 | 
			
		||||
                'name'	=> $commonName,                                         // Common name (as retrieved from AD)
 | 
			
		||||
                'fp'    => base64_encode(json_encode(get_browser(null, True)))  // Fingerprint (based on `HTTP_USER_AGENT`)
 | 
			
		||||
			];
 | 
			
		||||
 | 
			
		||||
			$secureToken = JWT::encode($jwtPayload, base64_decode($settings->JWT['PrivateKey_base64']));
 | 
			
		||||
@@ -121,8 +122,8 @@ function validateToken (string $secureToken) {
 | 
			
		||||
		WHERE LOWER(User.Username) = :username
 | 
			
		||||
	');
 | 
			
		||||
	$pdoQuery->execute([
 | 
			
		||||
			':username'	=>	(string) strtolower($jwtPayload->sub)
 | 
			
		||||
		]);
 | 
			
		||||
		':username'	=>	(string) strtolower($jwtPayload->sub)
 | 
			
		||||
	]);
 | 
			
		||||
	foreach($pdoQuery->fetchAll(PDO::FETCH_ASSOC) as $row) {
 | 
			
		||||
		try {
 | 
			
		||||
			$storedTokens[] = JWT::decode($row['Value'], base64_decode($settings->JWT['PrivateKey_base64']), $settings->JWT['Algorithm']);
 | 
			
		||||
@@ -136,7 +137,9 @@ function validateToken (string $secureToken) {
 | 
			
		||||
	if (!empty($storedTokens) && sizeof(array_filter($storedTokens, function ($value) use ($jwtPayload) {
 | 
			
		||||
		return $value->iat === $jwtPayload->iat;
 | 
			
		||||
	})) === 1) {
 | 
			
		||||
		return [
 | 
			
		||||
        purgeTokens($currentUserId, $settings->Session['Duration']);
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
			'status'	=> 'Success',
 | 
			
		||||
			'name'		=> $jwtPayload->name,
 | 
			
		||||
            'uid'       => $currentUserId
 | 
			
		||||
@@ -149,4 +152,54 @@ function validateToken (string $secureToken) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function purgeTokens(int $userID, int $maximumTokenAge) {
 | 
			
		||||
	global $settings, $pdoDB;
 | 
			
		||||
 | 
			
		||||
    $defunctTokens = []; $expiredTokens = [];
 | 
			
		||||
 | 
			
		||||
    $pdoQuery = $pdoDB->prepare('
 | 
			
		||||
		SELECT SecureToken.Id, SecureToken.Value
 | 
			
		||||
		FROM SecureToken
 | 
			
		||||
		WHERE SecureToken.UserId = :userid
 | 
			
		||||
	');
 | 
			
		||||
	$pdoQuery->execute([
 | 
			
		||||
		':userid'	=>	(int) $userID
 | 
			
		||||
	]);
 | 
			
		||||
	foreach($pdoQuery->fetchAll(PDO::FETCH_ASSOC) as $row) {
 | 
			
		||||
		try {
 | 
			
		||||
            $token = JWT::decode($row['Value'], base64_decode($settings->JWT['PrivateKey_base64']), $settings->JWT['Algorithm']);
 | 
			
		||||
            if ($token->iat < (time() - $maximumTokenAge)) {
 | 
			
		||||
                $expiredTokens[] = $row['Id'];
 | 
			
		||||
            }
 | 
			
		||||
		} catch (Exception $e) {
 | 
			
		||||
			$defunctTokens[] = $row['Id'];
 | 
			
		||||
		}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        // Sadly, PDO does not support named parameters in constructions like 'IN ( :array )'
 | 
			
		||||
        //   instead, the supported syntax is unnamed placeholders like 'IN (?, ?, ?, ...)'
 | 
			
		||||
        $pdoQuery = $pdoDB->prepare('
 | 
			
		||||
            DELETE FROM SecureToken
 | 
			
		||||
            WHERE SecureToken.Id IN (' . implode( ',', array_fill(0, count(array_merge($defunctTokens, $expiredTokens)), '?')) . ')
 | 
			
		||||
        ');
 | 
			
		||||
        $pdoQuery->execute(array_merge($defunctTokens, $expiredTokens));
 | 
			
		||||
 | 
			
		||||
        if ($settings->Debug['LogToFile']) {
 | 
			
		||||
            file_put_contents('../purgeToken.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- Garbage collection succeeded (' . $userID . ' => ' . $pdoQuery->rowCount() . ')' . PHP_EOL, FILE_APPEND);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'status'    => 'Success',
 | 
			
		||||
            'amount'    => $pdoQuery->rowCount()
 | 
			
		||||
        ];
 | 
			
		||||
    } catch (Exception $e) {
 | 
			
		||||
        if ($settings->Debug['LogToFile']) {
 | 
			
		||||
            file_put_contents('../purgeToken.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- Garbage collection failed (' . $userID . ' => ' . $e . ')' . PHP_EOL, FILE_APPEND);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ['status' => 'Fail', 'reason' => $e];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
?>
 | 
			
		||||
@@ -130,6 +130,22 @@ $contentLayout['manage']['section'] = <<<'MANAGE_SECTION'
 | 
			
		||||
                    %1$s
 | 
			
		||||
                  </tbody>
 | 
			
		||||
                </table>
 | 
			
		||||
                <div id="sessions">
 | 
			
		||||
                  <span data-translation="label_sessions" style="float: left; margin-left: 5px;">Sessions</span>
 | 
			
		||||
                  <br>
 | 
			
		||||
                  <table id="sessiontable">
 | 
			
		||||
                    <thead>
 | 
			
		||||
                      <tr>
 | 
			
		||||
                        <th data-translation="th_timestamp">Timestamp</th>
 | 
			
		||||
                        <th data-translation="th_origin"><origin></th>
 | 
			
		||||
                        <th data-translation="th_description">Description</th>
 | 
			
		||||
                        <th data-translation="th_manage">Manage</th>
 | 
			
		||||
                      </tr>
 | 
			
		||||
                    </thead>
 | 
			
		||||
                    <tbody>
 | 
			
		||||
                    </tbody>
 | 
			
		||||
                  </table>
 | 
			
		||||
                </div>
 | 
			
		||||
              </li>
 | 
			
		||||
            </ul>
 | 
			
		||||
MANAGE_SECTION;
 | 
			
		||||
 
 | 
			
		||||
@@ -9,23 +9,36 @@
 | 
			
		||||
 | 
			
		||||
	if ($validateTokenResult['status'] === "Success") {
 | 
			
		||||
        if ($_REQUEST['do'] === 'retrievesessions') {
 | 
			
		||||
            $storedTokens = [];
 | 
			
		||||
 | 
			
		||||
            $pdoQuery = $pdoDB->prepare('
 | 
			
		||||
                SELECT SecureToken.Id, SecureToken.UserId, SecureToken.Value
 | 
			
		||||
                FROM SecureToken
 | 
			
		||||
                WHERE SecureToken.Id = :userid
 | 
			
		||||
                WHERE SecureToken.UserId = :userid
 | 
			
		||||
            ');
 | 
			
		||||
            $pdoQuery->execute([
 | 
			
		||||
                ':userid'	=>	(int) $_REQUEST['userid']
 | 
			
		||||
            ]);
 | 
			
		||||
            foreach($pdoQuery->fetchAll(PDO::FETCH_ASSOC) as $row) {
 | 
			
		||||
                //bla
 | 
			
		||||
                try {
 | 
			
		||||
                    $JWTPayload = JWT::decode($row['Value'], base64_decode($settings->JWT['PrivateKey_base64']), $settings->JWT['Algorithm']);
 | 
			
		||||
                    $storedTokens[] = [
 | 
			
		||||
                        'iat'	=> $JWTPayload->iat,
 | 
			
		||||
                        'iss'	=> $JWTPayload->iss,
 | 
			
		||||
                        'fp'    => $JWTPayload->fp
 | 
			
		||||
                    ];
 | 
			
		||||
                } catch (Exception $e) {
 | 
			
		||||
                    // Invalid token
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Return JSON object
 | 
			
		||||
			header('Content-Type: application/json');
 | 
			
		||||
			echo json_encode([
 | 
			
		||||
				"Result"       => "Success",
 | 
			
		||||
                "UserSessions" => json_encode( $moo )
 | 
			
		||||
                "SessionCount" => sizeof($storedTokens),
 | 
			
		||||
                "UserSessions" => json_encode($storedTokens)
 | 
			
		||||
			]);
 | 
			
		||||
        } else {
 | 
			
		||||
            // No action requested, default action
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,43 @@ $(document).ready(function(){
 | 
			
		||||
    // Initialize the editable-table functionality
 | 
			
		||||
    $('#usertable').editableTableWidget();
 | 
			
		||||
 | 
			
		||||
    // Prevent clicking *through* popup-screens
 | 
			
		||||
    $('#sessions').click(function(event) {
 | 
			
		||||
        event.stopPropagation();
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    // Add eventhandlers to buttons
 | 
			
		||||
    $('#usertable button.session').click(function() {
 | 
			
		||||
        event.stopPropagation();
 | 
			
		||||
        $('#sessions tbody').empty();
 | 
			
		||||
        $('#sessions').fadeToggle();
 | 
			
		||||
 | 
			
		||||
        $.post("lucidAuth.manage.php", {
 | 
			
		||||
			do: "retrievesessions",
 | 
			
		||||
			userid: $(this).closest('tr').find('td:nth-child(1)').data('userid')
 | 
			
		||||
		})
 | 
			
		||||
		.done(function(data,_status) {
 | 
			
		||||
			if (data.Result === 'Success') {
 | 
			
		||||
                var Sessions = JSON.parse(data.UserSessions);
 | 
			
		||||
                for (var i = 0; i < data.SessionCount; i++) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        var Fingerprint = JSON.parse(atob(Sessions[i]['fp']));
 | 
			
		||||
                    } catch(e) {
 | 
			
		||||
                        // Do nothing
 | 
			
		||||
                    } 
 | 
			
		||||
                    $('#sessiontable tbody').append($('<tr>')
 | 
			
		||||
                        .append($('<td>', {
 | 
			
		||||
                            text: new Date(Sessions[i]['iat'] * 1000).toLocaleString('en-GB')
 | 
			
		||||
                        }))
 | 
			
		||||
                        .append($('<td>', {
 | 
			
		||||
                            text: Sessions[i]['iss']
 | 
			
		||||
                        }))
 | 
			
		||||
                        .append($('<td>', {
 | 
			
		||||
//                            text: Sessions[i]['fp'] ? atob(Sessions[i]['fp'])['browser'] + '(' + atob(Sessions[i]['fp'])['platform'] + ')' : ''
 | 
			
		||||
                            text: Fingerprint ? Fingerprint['browser'] + ' (' + Fingerprint['platform'] + ')' : ''
 | 
			
		||||
                        }))
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
			} else {
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
@@ -107,4 +136,8 @@ console.log({'new': newEntries, 'removed': removedEntries});
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$(document).click(function () {
 | 
			
		||||
    $('#sessions').fadeOut();
 | 
			
		||||
});
 | 
			
		||||
@@ -7,7 +7,8 @@ var locales = {
 | 
			
		||||
		button_delete:				"delete",
 | 
			
		||||
		button_login:				"login",
 | 
			
		||||
		heading_error:				"ERROR!",
 | 
			
		||||
		label_password:				"Password:",
 | 
			
		||||
        label_password:				"Password:",
 | 
			
		||||
        label_sessions:				"Sessions",
 | 
			
		||||
		label_username:				"Username:",
 | 
			
		||||
		link_logout:				"Logout",
 | 
			
		||||
		span_credentialsavailable:	"Login credentials available upon request!",
 | 
			
		||||
@@ -24,7 +25,8 @@ var locales = {
 | 
			
		||||
		button_delete:				"verwijder",
 | 
			
		||||
		button_login:				"log in",
 | 
			
		||||
		heading_error:				"FOUT!",
 | 
			
		||||
		label_password:				"Wachtwoord:",
 | 
			
		||||
        label_password:				"Wachtwoord:",
 | 
			
		||||
        label_sessions:				"Sessies",
 | 
			
		||||
		label_username:				"Gebruikersnaam:",
 | 
			
		||||
		link_logout:				"Log uit",
 | 
			
		||||
		span_credentialsavailable:	"Inloggegevens verkrijgbaar op aanvraag!",
 | 
			
		||||
 
 | 
			
		||||
@@ -129,6 +129,23 @@ body {
 | 
			
		||||
            .main section .buttons button {
 | 
			
		||||
                margin-left: inherit;
 | 
			
		||||
            }
 | 
			
		||||
        .main section #sessions {
 | 
			
		||||
			display: none;
 | 
			
		||||
			position: absolute;
 | 
			
		||||
			top: calc(50% - 160px);
 | 
			
		||||
			right: 20%;
 | 
			
		||||
			height: 320px;
 | 
			
		||||
			width: 60%;
 | 
			
		||||
			border: 1px solid rgb(0, 51, 153);
 | 
			
		||||
			box-shadow: black 0px 0px 20px;
 | 
			
		||||
			box-sizing: border-box;
 | 
			
		||||
	    	padding-top: 5px;
 | 
			
		||||
			background: white;
 | 
			
		||||
	    	font-size: inherit;
 | 
			
		||||
	    	font-weight: bold;
 | 
			
		||||
            z-index: 99;
 | 
			
		||||
            overflow-y: auto;
 | 
			
		||||
        }
 | 
			
		||||
        .main section table {
 | 
			
		||||
            width: 100%;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user