first commit

This commit is contained in:
2018-08-01 16:00:05 +02:00
commit 944fd76206
3205 changed files with 356767 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 by Gabriel Birke <gb@birke-software.de>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+40
View File
@@ -0,0 +1,40 @@
# Secure "Remember Me"
This library implements the best practices for implementing a secure
"Remember Me" functionality on web sites. Login information and unique secure
tokens are stored in a cookie. If the user visits the site, the login information
from the cookie is compared to information stored on the server. If the tokens
match, the user is logged in. A user can have login cookies on several
computers/browsers.
This library is heavily inspired by Barry Jaspan's article
"[Improved Persistent Login Cookie Best Practice][1]". The library protects
against the following attack scenarios:
- The computer of a user is stolen or compromised, enabling the attacker to log
in with the existing "Remember Me" cookie. The user knows this has happened.
The user can remotely invalidate all login cookies.
- An attacker has obtained the "Remember Me" cookie and has logged in with it.
The user does not know this. The next time he tries to log in with the cookie
that was stolen, he gets a warning and all login cookies are invalidated.
- An attacker has obtained the database of login tokens from the server. The
stored tokens are hashed so he can't use them without computational effort
(rainbow tables or brute force).
## Installation
composer require birke/rememberme
## Usage example
See the `example` directory for an example.
## Improving security
The generated tokens are pseudo-random and the storage classes use the SHA1 algorithm
to hash them. If you need better security than that, overwrite the
`Authenticator::generateToken` method to generate a truly random token. If you are
using PHP >=5.5 you can use the "[password_hash][2]" and "[password_verify][3]" functions.
On lower PHP versions you could use the [userland implementations][4] of these functions.
[1]: http://jaspan.com/improved%5Fpersistent%5Flogin%5Fcookie%5Fbest%5Fpractice
[2]: http://www.php.net/manual/en/function.password-hash.php
[3]: http://www.php.net/manual/en/function.password-verify.php
[4]: https://github.com/ircmaxell/password_compat
@@ -0,0 +1,24 @@
{
"name": "birke/rememberme",
"version": "1.0.5",
"description": "Secure \"Remember Me\" functionality",
"keywords": [ "cookie", "remember", "security"],
"homepage": "https://github.com/gbirke/rememberme",
"license": "MIT",
"authors": [
{
"name": "Gabriel Birke",
"email": "gb@birke-software.de"
}
],
"minimum-stability": "stable",
"autoload": {
"psr-4": {"Birke\\": "src/"}
},
"require": {
"paragonie/random_compat": "^1.1.4"
},
"require-dev": {
"phpunit/phpunit": "4.*"
}
}
@@ -0,0 +1,119 @@
<?php
/**
* This file demonstrates how to use the Rememberme library.
*
* Some code (autoload, templating) is just simple boilerplate and no shining
* example of how to write php applications.
*
* @author Gabriel Birke
*/
require_once __DIR__.'/../vendor/autoload.php';
use Birke\Rememberme;
/**
* Helper function for redirecting and destroying the session
* @param bool $destroySession
* @return void
*/
function redirect($destroySession=false) {
if($destroySession) {
session_regenerate_id(true);
session_destroy();
}
header("Location: index.php");
exit;
}
// Normally you would store the credentials in a DB
$username = "demo";
$password = "demo";
// Initialize RememberMe Library with file storage
$storagePath = dirname(__FILE__)."/tokens";
if(!is_writable($storagePath) || !is_dir($storagePath)) {
die("'$storagePath' does not exist or is not writable by the web server.
To run the example, please create the directory and give it the
correct permissions.");
}
$storage = new Rememberme\Storage\File($storagePath);
$rememberMe = new Rememberme\Authenticator($storage);
// First, we initialize the session, to see if we are already logged in
session_start();
if(!empty($_SESSION['username'])) {
if(!empty($_GET['logout'])) {
$rememberMe->clearCookie($_SESSION['username']);
redirect(true);
}
if(!empty($_GET['completelogout'])) {
$storage->cleanAllTriplets($_SESSION['username']);
redirect(true);
}
// Check, if the Rememberme cookie exists and is still valid.
// If not, we log out the current session
if(!empty($_COOKIE[$rememberMe->getCookieName()]) && !$rememberMe->cookieIsValid()) {
redirect(true);
}
// User is still logged in - show content
$content = tpl("user_is_logged_in");
}
// If we are not logged in, try to log in via Rememberme cookie
else {
// If we can present the correct tokens from the cookie, we are logged in
$loginresult = $rememberMe->login();
if($loginresult) {
$_SESSION['username'] = $loginresult;
// There is a chance that an attacker has stolen the login token, so we store
// the fact that the user was logged in via RememberMe (instead of login form)
$_SESSION['remembered_by_cookie'] = true;
redirect();
}
else {
// If $rememberMe returned false, check if the token was invalid
if($rememberMe->loginTokenWasInvalid()) {
$content = tpl("cookie_was_stolen");
}
// $rememberMe returned false because of invalid/missing Rememberme cookie - normal login process
else {
if(!empty($_POST)) {
if($username == $_POST['username'] && $password == $_POST['password']) {
session_regenerate_id();
$_SESSION['username'] = $username;
// If the user wants to be remembered, create Rememberme cookie
if(!empty($_POST['rememberme'])) {
$rememberMe->createCookie($username);
}
else {
$rememberMe->clearCookie();
}
redirect();
}
else {
$content = tpl("login", "Invalid credentials");
}
}
else {
$content = tpl("login");
}
}
}
}
// template function for including content, nothing interesting
function tpl($template, $msg="") {
$fn = __DIR__ . DIRECTORY_SEPARATOR . "templates" . DIRECTORY_SEPARATOR . $template . ".php";
if(file_exists($fn)) {
ob_start();
include $fn;
return ob_get_clean();
}
else {
return "Template $fn not found";
}
}
@@ -0,0 +1,157 @@
/* HTML5 ✰ Boilerplate */
html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure, footer, header, hgroup,
menu, nav, section, summary, time, mark, audio, video {
margin:0;
padding:0;
border:0;
outline:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
display:block;
}
nav ul { list-style:none; }
blockquote, q { quotes:none; }
blockquote:before, blockquote:after,
q:before, q:after { content:''; content:none; }
a { margin:0; padding:0; font-size:100%; vertical-align:baseline; background:transparent; }
ins { background-color:#ff9; color:#000; text-decoration:none; }
mark { background-color:#ff9; color:#000; font-style:italic; font-weight:bold; }
del { text-decoration: line-through; }
abbr[title], dfn[title] { border-bottom:1px dotted; cursor:help; }
table { border-collapse:collapse; border-spacing:0; }
hr { display:block; height:1px; border:0; border-top:1px solid #ccc; margin:1em 0; padding:0; }
input, select { vertical-align:middle; }
body { font:13px/1.231 sans-serif; *font-size:small; }
select, input, textarea, button { font:99% sans-serif; }
pre, code, kbd, samp { font-family: monospace, sans-serif; }
body, select, input, textarea { color: #444; }
h1,h2,h3,h4,h5,h6 { font-weight: bold; }
html { overflow-y: scroll; }
a:hover, a:active { outline: none; }
a, a:active, a:visited { color: #607890; }
a:hover { color: #036; }
ul, ol { margin-left: 1.8em; }
ol { list-style-type: decimal; }
nav ul, nav li { margin: 0; }
small { font-size: 85%; }
strong, th { font-weight: bold; }
td, td img { vertical-align: top; }
sub { vertical-align: sub; font-size: smaller; }
sup { vertical-align: super; font-size: smaller; }
pre { padding: 15px; white-space: pre; white-space: pre-wrap; white-space: pre-line; word-wrap: break-word; }
textarea { overflow: auto; }
.ie6 legend, .ie7 legend { margin-left: -7px; }
input[type="radio"] { vertical-align: text-bottom; }
input[type="checkbox"] { vertical-align: bottom; }
.ie7 input[type="checkbox"] { vertical-align: baseline; }
.ie6 input { vertical-align: text-bottom; }
label, input[type=button], input[type=submit], button { cursor: pointer; }
button, input, select, textarea { margin: 0; }
input:valid, textarea:valid { }
input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; }
.no-boxshadow input:invalid,
.no-boxshadow textarea:invalid { background-color: #f0dddd; }
::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; }
::selection { background:#FF5E99; color:#fff; text-shadow: none; }
a:link { -webkit-tap-highlight-color: #FF5E99; }
button { width: auto; overflow: visible; }
.ie7 img { -ms-interpolation-mode: bicubic; }
.ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; }
.hidden { display: none; visibility: hidden; }
.visuallyhidden { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px, 1px, 1px, 1px); }
.invisible { visibility: hidden; }
.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; visibility: hidden; }
.clearfix:after { clear: both; }
.clearfix { zoom: 1; }
/* Primary Styles
Author:
*/
h1 {
width:800px;
margin:50px auto;
text-align:center;
font-size:200%;
font-weight:bold;
}
#main {
width:800px;
margin:50px auto;
}
label {
display:inline-block;
width:8em;
}
input {
margin:5px;
}
p {
margin-bottom:1em;
}
ol {
margin-bottom:1em;
padding-left:1.5em;
}
@media all and (orientation:portrait) {
}
@media all and (orientation:landscape) {
}
@media screen and (max-device-width: 480px) {
/* html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */
}
@media print {
* { background: transparent !important; color: #444 !important; text-shadow: none !important; }
a, a:visited { color: #444 !important; text-decoration: underline; }
a:after { content: " (" attr(href) ")"; }
abbr:after { content: " (" attr(title) ")"; }
.ir a:after { content: ""; }
pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
thead { display: table-header-group; }
tr, img { page-break-inside: avoid; }
@page { margin: 0.5cm; }
p, h2, h3 { orphans: 3; widows: 3; }
h2, h3{ page-break-after: avoid; }
}
@@ -0,0 +1,42 @@
<?php
// Include PHP before any content is generated to we can set cookies
// Sets the $content variable with the dynamic page content
include "./action.php";
?>
<!doctype html>
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
<!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
<!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
<!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Rememberme PHP library test</title>
<meta name="author" content="Gabriel Birke">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/style.css?v=2">
</head>
<body>
<div id="container">
<header>
<h1>Rememberme PHP library test</h1>
</header>
<div id="main">
<?php
// Output generated content
echo $content;
?>
</div>
<footer>
</footer>
</div> <!-- end of #container -->
</body>
</html>
@@ -0,0 +1,4 @@
<p class="error">Someone else has used your login information to acccess this page!<br>
All sessions were logged out. <br>
Please log in with your credentials and check your data.</p>
<p><a href="index.php">To login form</a></p>
@@ -0,0 +1,14 @@
<?php if(!empty($msg)) echo "<p class='msg'>$msg</p>"; ?>
<p>This is the demo for logging in with the Rememberme Library. <br>
You are seeing this form because you have no active "Remember me" cookie and no
credentials stored in the session.
</p>
<p>Please log in with the username and password <em>demo</em></p>
<form method="post" action="index.php">
<label for="username">User Name:</label> <input type="text" name="username" id="username"> <br>
<label for="password">Password:</label> <input type="password" name="password" id="password"><br>
<input type="checkbox" id="rememberme" value="1" name="rememberme"> Remember me <br>
<input type="submit" value="Log me in">
</form>
@@ -0,0 +1,31 @@
<p>You are logged in as <strong><?php echo $_SESSION['username']; ?></strong></p>
<p>Your session ID is <strong><?php echo session_id(); ?></strong></p>
<?php if(!empty($_COOKIE['PHP_REMEMBERME'])): ?>
<p>The remember me cookie is active.
Cookie value is <code><?php echo $_COOKIE['PHP_REMEMBERME']; ?></code></p>
<?php else: ?>
<p>The remember me cookie is not active.</p>
<?php endif; ?>
<?php if(!empty($_SESSION['remembered_by_cookie'])): ?>
<p>You were logged in with the "Remember me" cookie. In a real application
you should ask the user for his credentials before allowing him anything
"dangerous" like changing the login information, accessing sensitive data
or making a payment.</p>
<?php endif; ?>
<p>If you want to test the warning when a possible identity theft is detected, try the following steps:</p>
<ol>
<li>Login to this page with a non-Firefox Browser. In the following steps I
will call that browser "Chrome" :) <br>
Make sure to check the "Remember me" checkbox when logging in.</li>
<li>Copy the cookie value from above into the clipboard.</li>
<li>Quit Chrome to end the session.</li>
<li>Start Firefox and install the <a href="https://addons.mozilla.org/de/firefox/addon/6683/">Firecookie</a> extension if needed.</li>
<li>Show this page. If you see this text, log out. You should see the login form.</li>
<li>Create the <code>PHP_REMEMBERME</code> cookie with the value you copied.</li>
<li>Refresh the page - you are now logged in and should see this text. You have stolen the login credential from Chrome!</li>
<li>Start Chrome and try to show this page - you should get a warning instead of the login dialog.</li>
<li>Refresh this page in Firefox - You are logged out.</li>
</ol>
<p><a href="index.php?logout=true">Log out in this browser window.</a></p>
<p><a href="index.php?completelogout=true">Log out from <strong>all</strong>
sessions in all browser windows where the "Remember me" cookie is active.</a></p>
@@ -0,0 +1,7 @@
<phpunit bootstrap="test/bootstrap.php">
<testsuites>
<testsuite name="Rememberme">
<directory>test</directory>
</testsuite>
</testsuites>
</phpunit>
@@ -0,0 +1,293 @@
<?php
namespace Birke\Rememberme;
class Authenticator
{
/**
* @var string
*/
protected $cookieName = "PHP_REMEMBERME";
/**
* @var Cookie
*/
protected $cookie;
/**
* @var Storage\StorageInterface
*/
protected $storage;
/**
* Number of seconds in the future the cookie and storage will expire (defaults to 1 week)
* @var int
*/
protected $expireTime = 604800;
/**
* If the return from the storage was Birke\Rememberme\Storage\StorageInterface::TRIPLET_INVALID,
* this is set to true
* @var bool
*/
protected $lastLoginTokenWasInvalid = false;
/**
* If the login token was invalid, delete all login tokens of this user
* @var bool
*/
protected $cleanStoredTokensOnInvalidResult = true;
/**
* Additional salt to add more entropy when the tokens are stored as hashes.
* @var string
*/
protected $salt = "";
/**
* @param Storage\StorageInterface $storage
*/
public function __construct(Storage\StorageInterface $storage)
{
$this->storage = $storage;
$this->cookie = new Cookie();
}
/**
* Check Credentials from cookie. Returns false if login was not successful, credential string if it was successful
* @return bool|string
*/
public function login()
{
$cookieValues = $this->getCookieValues();
if (!$cookieValues) {
return false;
}
$loginResult = false;
switch ($this->storage->findTriplet($cookieValues[0], $cookieValues[1] . $this->salt, $cookieValues[2] . $this->salt)) {
case Storage\StorageInterface::TRIPLET_FOUND:
$expire = time() + $this->expireTime;
$newToken = $this->createToken();
$this->storage->replaceTriplet($cookieValues[0], $newToken . $this->salt, $cookieValues[2] . $this->salt, $expire);
$this->cookie->setCookie($this->cookieName, implode("|", array($cookieValues[0], $newToken, $cookieValues[2])), $expire);
$loginResult = $cookieValues[0];
break;
case Storage\StorageInterface::TRIPLET_INVALID:
$this->cookie->setCookie($this->cookieName, "", time() - $this->expireTime);
$this->lastLoginTokenWasInvalid = true;
if ($this->cleanStoredTokensOnInvalidResult) {
$this->storage->cleanAllTriplets($cookieValues[0]);
}
break;
}
return $loginResult;
}
/**
* @return bool
*/
public function cookieIsValid()
{
$cookieValues = $this->getCookieValues();
if (!$cookieValues) {
return false;
}
$state = $this->storage->findTriplet($cookieValues[0], $cookieValues[1] . $this->salt, $cookieValues[2] . $this->salt);
return $state == Storage\StorageInterface::TRIPLET_FOUND;
}
/**
* @param $credential
* @return $this
*/
public function createCookie($credential)
{
$newToken = $this->createToken();
$newPersistentToken = $this->createToken();
$expire = time() + $this->expireTime;
$this->storage->storeTriplet($credential, $newToken . $this->salt, $newPersistentToken . $this->salt, $expire);
$this->cookie->setCookie($this->cookieName, implode("|", array($credential, $newToken, $newPersistentToken)), $expire);
return $this;
}
/**
* Expire the rememberme cookie, unset $_COOKIE[$this->cookieName] value and
* remove current login triplet from storage.
* @param boolean $clearFromStorage
* @return boolean
*/
public function clearCookie($clearFromStorage = true)
{
if (empty($_COOKIE[$this->cookieName])) {
return false;
}
$cookieValues = explode("|", $_COOKIE[$this->cookieName], 3);
$this->cookie->setCookie($this->cookieName, "", time() - $this->expireTime);
unset($_COOKIE[$this->cookieName]);
if (!$clearFromStorage) {
return true;
}
if (count($cookieValues) < 3) {
return false;
}
$this->storage->cleanTriplet($cookieValues[0], $cookieValues[2] . $this->salt);
return true;
}
/**
* @return string
*/
public function getCookieName()
{
return $this->cookieName;
}
/**
* @param $name
* @return $this
*/
public function setCookieName($name)
{
$this->cookieName = $name;
return $this;
}
/**
* @param Cookie $cookie
* @return $this
*/
public function setCookie(Cookie $cookie)
{
$this->cookie = $cookie;
return $this;
}
/**
* @return bool
*/
public function loginTokenWasInvalid()
{
return $this->lastLoginTokenWasInvalid;
}
/**
* @return Cookie
*/
public function getCookie()
{
return $this->cookie;
}
/**
* @param $state
* @return Authenticator
*/
public function setCleanStoredTokensOnInvalidResult($state)
{
$this->cleanStoredTokensOnInvalidResult = $state;
return $this;
}
/**
* @return bool
*/
public function getCleanStoredTokensOnInvalidResult()
{
return $this->cleanStoredTokensOnInvalidResult;
}
/**
* Create a pseudo-random token.
*
* The token is pseudo-random. If you need better security, read from /dev/urandom
*/
protected function createToken()
{
return bin2hex(random_bytes(32));
}
/**
* @return array
*/
protected function getCookieValues()
{
// Cookie was not sent with incoming request
if (empty($_COOKIE[$this->cookieName])) {
return array();
}
$cookieValues = explode("|", $_COOKIE[$this->cookieName], 3);
if (count($cookieValues) < 3) {
return array();
}
return $cookieValues;
}
/**
* Return how many seconds in the future that the cookie will expire
* @return int
*/
public function getExpireTime()
{
return $this->expireTime;
}
/**
* @param int $expireTime How many seconds in the future the cookie will expire
*
* Default is 604800 (1 week)
*
* @return Authenticator
*/
public function setExpireTime($expireTime)
{
$this->expireTime = $expireTime;
return $this;
}
/**
*
* @return string
*/
public function getSalt()
{
return $this->salt;
}
/**
* The salt is additional information that is added to the tokens to make
* them more unqiue and secure. The salt is not stored in the cookie and
* should not saved in the storage.
*
* For example, to bind a token to an IP address use $_SERVER['REMOTE_ADDR'].
* To bind a token to the browser (user agent), use $_SERVER['HTTP_USER_AGENT].
* You could also use a long random string that is uniqe to your application.
* @param string $salt
*/
public function setSalt($salt)
{
$this->salt = $salt;
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace Birke\Rememberme;
/**
* Wrapper around setcookie function for better testability
*/
class Cookie
{
/**
* @var string
*/
protected $path = "";
/**
* @var string
*/
protected $domain = "";
/**
* @var bool
*/
protected $secure = false;
/**
* @var bool
*/
protected $httpOnly = true;
/**
* @param $name
* @param string $value
* @param int $expire
* @return bool
*/
public function setCookie($name, $value = "", $expire = 0)
{
return setcookie($name, $value, $expire, $this->path, $this->domain, $this->secure, $this->httpOnly);
}
/**
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* @param $path
*/
public function setPath($path)
{
$this->path = $path;
}
/**
* @return string
*/
public function getDomain()
{
return $this->domain;
}
/**
* @param $domain
*/
public function setDomain($domain)
{
$this->domain = $domain;
}
/**
* @return bool
*/
public function getSecure()
{
return $this->secure;
}
/**
* @param $secure
*/
public function setSecure($secure)
{
$this->secure = $secure;
}
/**
* @return bool
*/
public function getHttpOnly()
{
return $this->httpOnly;
}
/**
* @param $httponly
*/
public function setHttpOnly($httponly)
{
$this->httpOnly = $httponly;
}
}
+145
View File
@@ -0,0 +1,145 @@
<?php
namespace Birke\Rememberme\Storage;
/**
* This abstract class contains properties with getters and setters for all
* database storage classes
*
* @author Gabriel Birke
*/
abstract class DB implements StorageInterface
{
/**
*
* @var string
*/
protected $tableName = "";
/**
*
* @var string
*/
protected $credentialColumn = "";
/**
*
* @var string
*/
protected $tokenColumn = "";
/**
*
* @var string
*/
protected $persistentTokenColumn = "";
/**
*
* @var string
*/
protected $expiresColumn = "";
/**
* @param $options
*/
public function __construct($options)
{
foreach ($options as $prop => $value) {
$setter = "set" . ucfirst($prop);
if (method_exists($this, $setter)) {
$this->$setter($value);
}
}
}
/**
* @return string
*/
public function getTableName()
{
return $this->tableName;
}
/**
* @param $tableName
* @return $this
*/
public function setTableName($tableName)
{
$this->tableName = $tableName;
return $this;
}
/**
* @return string
*/
public function getCredentialColumn()
{
return $this->credentialColumn;
}
/**
* @param $credentialColumn
* @return $this
*/
public function setCredentialColumn($credentialColumn)
{
$this->credentialColumn = $credentialColumn;
return $this;
}
/**
* @return string
*/
public function getTokenColumn()
{
return $this->tokenColumn;
}
/**
* @param $tokenColumn
* @return $this
*/
public function setTokenColumn($tokenColumn)
{
$this->tokenColumn = $tokenColumn;
return $this;
}
/**
* @return string
*/
public function getPersistentTokenColumn()
{
return $this->persistentTokenColumn;
}
/**
* @param $persistentTokenColumn
* @return $this
*/
public function setPersistentTokenColumn($persistentTokenColumn)
{
$this->persistentTokenColumn = $persistentTokenColumn;
return $this;
}
/**
* @return string
*/
public function getExpiresColumn()
{
return $this->expiresColumn;
}
/**
* @param $expiresColumn
* @return $this
*/
public function setExpiresColumn($expiresColumn)
{
$this->expiresColumn = $expiresColumn;
return $this;
}
}
@@ -0,0 +1,119 @@
<?php
namespace Birke\Rememberme\Storage;
/**
* File-Based Storage
*/
class File implements StorageInterface
{
/**
* @var string
*/
protected $path = "";
/**
* @var string
*/
protected $suffix = ".txt";
/**
* @param string $path
* @param string $suffix
*/
public function __construct($path = "", $suffix = ".txt")
{
$this->path = $path;
$this->suffix = $suffix;
}
/**
* @param mixed $credential
* @param string $token
* @param string $persistentToken
* @return int
*/
public function findTriplet($credential, $token, $persistentToken)
{
// Hash the tokens, because they can contain a salt and can be accessed in the file system
$persistentToken = sha1($persistentToken);
$token = sha1($token);
$fn = $this->getFilename($credential, $persistentToken);
if (!file_exists($fn)) {
return self::TRIPLET_NOT_FOUND;
}
$fileToken = trim(file_get_contents($fn));
if ($fileToken == $token) {
return self::TRIPLET_FOUND;
}
return self::TRIPLET_INVALID;
}
/**
* @param mixed $credential
* @param string $token
* @param string $persistentToken
* @param int $expire
* @return $this
*/
public function storeTriplet($credential, $token, $persistentToken, $expire = 0)
{
// Hash the tokens, because they can contain a salt and can be accessed in the file system
$persistentToken = sha1($persistentToken);
$token = sha1($token);
$fn = $this->getFilename($credential, $persistentToken);
file_put_contents($fn, $token);
return $this;
}
/**
* @param mixed $credential
* @param string $persistentToken
*/
public function cleanTriplet($credential, $persistentToken)
{
$persistentToken = sha1($persistentToken);
$fn = $this->getFilename($credential, $persistentToken);
if (file_exists($fn)) {
unlink($fn);
}
}
/**
* Replace current token after successful authentication
* @param $credential
* @param $token
* @param $persistentToken
* @param int $expire
*/
public function replaceTriplet($credential, $token, $persistentToken, $expire = 0)
{
$this->cleanTriplet($credential, $persistentToken);
$this->storeTriplet($credential, $token, $persistentToken, $expire);
}
/**
* @param $credential
*/
public function cleanAllTriplets($credential)
{
foreach (glob($this->path . DIRECTORY_SEPARATOR . $credential . ".*" . $this->suffix) as $file) {
unlink($file);
}
}
/**
* @param $credential
* @param $persistentToken
* @return string
*/
protected function getFilename($credential, $persistentToken)
{
return $this->path . DIRECTORY_SEPARATOR . $credential . "." . $persistentToken . $this->suffix;
}
}
+121
View File
@@ -0,0 +1,121 @@
<?php
namespace Birke\Rememberme\Storage;
/**
* Store login tokens in database with PDO class
*
* @author birke
*/
class PDO extends DB
{
/**
* @var \PDO
*/
protected $connection;
/**
* @param mixed $credential
* @param string $token
* @param string $persistentToken
* @return int
*/
public function findTriplet($credential, $token, $persistentToken)
{
// We don't store the sha1 as binary values because otherwise we could not use
// proper XML test data
$sql = "SELECT IF(SHA1(?) = {$this->tokenColumn}, 1, -1) AS token_match " .
"FROM {$this->tableName} WHERE {$this->credentialColumn} = ? " .
"AND {$this->persistentTokenColumn} = SHA1(?) AND {$this->expiresColumn} > NOW() LIMIT 1";
$query = $this->connection->prepare($sql);
$query->execute(array($token, $credential, $persistentToken));
$result = $query->fetchColumn();
if (!$result) {
return self::TRIPLET_NOT_FOUND;
} elseif ($result == 1) {
return self::TRIPLET_FOUND;
}
return self::TRIPLET_INVALID;
}
/**
* @param mixed $credential
* @param string $token
* @param string $persistentToken
* @param int $expire
*/
public function storeTriplet($credential, $token, $persistentToken, $expire = 0)
{
$sql = "INSERT INTO {$this->tableName}({$this->credentialColumn}, " .
"{$this->tokenColumn}, {$this->persistentTokenColumn}, " .
"{$this->expiresColumn}) VALUES(?, SHA1(?), SHA1(?), ?)";
$query = $this->connection->prepare($sql);
$query->execute(array($credential, $token, $persistentToken, date("Y-m-d H:i:s", $expire)));
}
/**
* @param mixed $credential
* @param string $persistentToken
*/
public function cleanTriplet($credential, $persistentToken)
{
$sql = "DELETE FROM {$this->tableName} WHERE {$this->credentialColumn} = ? "
. "AND {$this->persistentTokenColumn} = SHA1(?)";
$query = $this->connection->prepare($sql);
$query->execute(array($credential, $persistentToken));
}
/**
* Replace current token after successful authentication
* @param $credential
* @param $token
* @param $persistentToken
* @param int $expire
*/
public function replaceTriplet($credential, $token, $persistentToken, $expire = 0)
{
try {
$this->connection->beginTransaction();
$this->cleanTriplet($credential, $persistentToken);
$this->storeTriplet($credential, $token, $persistentToken, $expire);
$this->connection->commit();
}
catch (\PDOException $e) {
$this->connection->rollBack();
throw $e;
}
}
/**
* @param $credential
*/
public function cleanAllTriplets($credential)
{
$sql = "DELETE FROM {$this->tableName} WHERE {$this->credentialColumn} = ? ";
$query = $this->connection->prepare($sql);
$query->execute(array($credential));
}
/**
* @return \PDO
*/
public function getConnection()
{
return $this->connection;
}
/**
* @param PDO $connection
*/
public function setConnection(\PDO $connection)
{
$this->connection = $connection;
}
}
@@ -0,0 +1,66 @@
<?php
namespace Birke\Rememberme\Storage;
/**
* This interface is for storing the credential/token/persistentToken triplets
*
* IMPORTANT SECURITY NOTICE: The storage should not store the token values in the clear.
* Always use a secure hash function!
*/
interface StorageInterface
{
const TRIPLET_FOUND = 1;
const TRIPLET_NOT_FOUND = 0;
const TRIPLET_INVALID = -1;
/**
* Return Tri-state value constant
*
* @param mixed $credential Unique credential (user id, email address, user name)
* @param string $token One-Time Token
* @param string $persistentToken Persistent Token
* @return int
*/
public function findTriplet($credential, $token, $persistentToken);
/**
* Store the new token for the credential and the persistent token.
* Create a new storage entry, if the combination of credential and persistent
* token does not exist.
*
* @param mixed $credential
* @param string $token
* @param string $persistentToken
* @param int $expire Timestamp when this triplet will expire (0=no expiry)
*/
public function storeTriplet($credential, $token, $persistentToken, $expire = 0);
/**
* Replace current token after successful authentication
* @param $credential
* @param $token
* @param $persistentToken
* @param int $expire
*/
public function replaceTriplet($credential, $token, $persistentToken, $expire = 0);
/**
* Remove one triplet of the user from the store
*
* @abstract
* @param mixed $credential
* @param string $persistentToken
* @return void
*/
public function cleanTriplet($credential, $persistentToken);
/**
* Remove all triplets of a user, effectively logging him out on all machines
*
* @abstract
* @param $credential
* @return void
*/
public function cleanAllTriplets($credential);
}
@@ -0,0 +1,31 @@
<?php
class CookieTest extends PHPUnit_Framework_TestCase
{
public function testDefaultValues()
{
$cookie = new \Birke\Rememberme\Cookie();
$this->assertEquals('', $cookie->getPath());
$this->assertEquals('', $cookie->getDomain());
$this->assertFalse($cookie->getSecure());
$this->assertTrue($cookie->getHttpOnly());
}
public function testSetters()
{
$cookie = new \Birke\Rememberme\Cookie();
$cookie->setPath('/test');
$this->assertEquals('/test', $cookie->getPath());
$cookie->setDomain('www.foo.com');
$this->assertEquals('www.foo.com', $cookie->getDomain());
$cookie->setSecure(true);
$this->assertTrue($cookie->getSecure());
$cookie->setHttpOnly(false);
$this->assertFalse($cookie->getHttpOnly());
}
}
+352
View File
@@ -0,0 +1,352 @@
<?php
class RemembermeTest extends PHPUnit_Framework_TestCase
{
/**
* @var Rememberme
*/
protected $rememberme;
/**
* Default user id, used as credential information to check
*/
protected $userid = 1;
protected $validToken = "78b1e6d775cec5260001af137a79dbd5";
protected $validPersistentToken = "0e0530c1430da76495955eb06eb99d95";
protected $invalidToken = "7ae7c7caa0c7b880cb247bb281d527de";
protected $cookie;
protected $storage;
function setUp() {
$this->storage = $this->getMockBuilder(\Birke\Rememberme\Storage\StorageInterface::class)->getMock();
$this->rememberme = new Birke\Rememberme\Authenticator($this->storage);
$this->cookie = $this->getMockBuilder(\Birke\Rememberme\Cookie::class)->setMethods(['setcookie'])->getMock();
$this->rememberme->setCookie($this->cookie);
$_COOKIE = array();
}
/* Basic cases */
public function testReturnFalseIfNoCookieExists()
{
$this->assertFalse($this->rememberme->login());
}
public function testReturnFalseIfCookieIsInvalid()
{
$_COOKIE = array($this->rememberme->getCookieName() => "DUMMY");
$this->assertFalse($this->rememberme->login());
$_COOKIE = array($this->rememberme->getCookieName() => $this->userid."|a");
$this->assertFalse($this->rememberme->login());
}
public function testLoginTriesToFindTripletWithValuesFromCookie() {
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("findTriplet")
->with($this->equalTo($this->userid), $this->equalTo($this->validToken), $this->equalTo($this->validPersistentToken));
$this->rememberme->login();
}
/* Success cases */
public function testReturnTrueIfTripletIsFound() {
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_FOUND));
$this->assertEquals($this->userid, $this->rememberme->login());
}
public function testStoreNewTripletInCookieIfTripletIsFound() {
$oldcookieValue = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$_COOKIE[$this->rememberme->getCookieName()] = $oldcookieValue;
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_FOUND));
$this->cookie->expects($this->once())
->method("setcookie")
->with(
$this->anything(),
$this->logicalAnd(
$this->matchesRegularExpression('/^'.$this->userid.'\|[a-f0-9]{32,}\|'.$this->validPersistentToken.'$/'),
$this->logicalNot($this->equalTo($oldcookieValue))
)
);
$this->rememberme->login();
}
public function testReplaceTripletInStorageIfTripletIsFound() {
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_FOUND));
$this->storage->expects($this->once())
->method("replaceTriplet")
->with(
$this->equalTo($this->userid),
$this->logicalAnd(
$this->matchesRegularExpression('/^[a-f0-9]{32,}$/'),
$this->logicalNot($this->equalTo($this->validToken))
),
$this->equalTo($this->validPersistentToken)
);
$this->rememberme->login();
}
public function testCookieContainsUserIDAndHexTokensIfTripletIsFound()
{
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_FOUND));
$this->cookie->expects($this->once())
->method("setcookie")
->with($this->anything(),
$this->matchesRegularExpression('/^'.$this->userid.'\|[a-f0-9]{32,}\|[a-f0-9]{32,}$/')
);
$this->rememberme->login();
}
public function testCookieContainsNewTokenIfTripletIsFound()
{
$oldcookieValue = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$_COOKIE[$this->rememberme->getCookieName()] = $oldcookieValue;
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_FOUND));
$this->cookie->expects($this->once())
->method("setcookie")
->with($this->anything(),
$this->logicalAnd(
$this->matchesRegularExpression('/^'.$this->userid.'\|[a-f0-9]{32,}\|'.$this->validPersistentToken.'$/'),
$this->logicalNot($this->equalTo($oldcookieValue))
)
);
$this->rememberme->login();
}
public function testCookieExpiryIsInTheFutureIfTripletIsFound()
{
$oldcookieValue = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$_COOKIE[$this->rememberme->getCookieName()] = $oldcookieValue;
$now = time();
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_FOUND));
$this->cookie->expects($this->once())
->method("setcookie")
->with($this->anything(), $this->anything(), $this->greaterThan($now));
$this->rememberme->login();
}
/* Failure Cases */
public function testFalseIfTripletIsNotFound() {
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_NOT_FOUND));
$this->assertFalse($this->rememberme->login());
}
public function testFalseIfTripletIsInvalid() {
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->invalidToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_INVALID));
$this->assertFalse($this->rememberme->login());
}
public function testCookieIsExpiredIfTripletIsInvalid() {
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->invalidToken, $this->validPersistentToken));
$now = time();
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_INVALID));
$this->cookie->expects($this->once())
->method("setcookie")
->with($this->anything(), $this->anything(), $this->lessThan($now));
$this->rememberme->login();
}
public function testAllStoredTokensAreClearedIfTripletIsInvalid() {
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->invalidToken, $this->validPersistentToken));
$this->storage->expects($this->any())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_INVALID));
$this->storage->expects($this->once())
->method("cleanAllTriplets")
->with($this->equalTo($this->userid));
$this->rememberme->setCleanStoredTokensOnInvalidResult(true);
$this->rememberme->login();
$this->rememberme->setCleanStoredTokensOnInvalidResult(false);
$this->rememberme->login();
}
public function testInvalidTripletStateIsStored() {
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->invalidToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_INVALID));
$this->assertFalse($this->rememberme->loginTokenWasInvalid());
$this->rememberme->login();
$this->assertTrue($this->rememberme->loginTokenWasInvalid());
}
/* Cookie tests */
public function testCookieNameCanBeSet() {
$cookieName = "myCustomName";
$this->rememberme->setCookieName($cookieName);
$_COOKIE[$cookieName] = implode("|", array($this->userid, $this->validToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_FOUND));
$this->cookie->expects($this->once())
->method("setcookie")
->with($this->equalTo($cookieName));
$this->assertEquals($this->userid, $this->rememberme->login());
}
public function testCookieIsSetToConfiguredExpiryDate() {
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$now = time();
$expireTime = 31556926; // 1 year
$this->rememberme->setExpireTime($expireTime);
$this->storage->expects($this->once())
->method("findTriplet")
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_FOUND));
$this->cookie->expects($this->once())
->method("setcookie")
->with($this->anything(), $this->anything(), $this->equalTo($now+$expireTime, 10));
$this->rememberme->login();
}
/* Salting test */
public function testSaltIsAddedToTokensOnLogin() {
$salt = "Mozilla Firefox 4.0";
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("findTriplet")
->with($this->equalTo($this->userid), $this->equalTo($this->validToken.$salt), $this->equalTo($this->validPersistentToken.$salt))
->will($this->returnValue(Birke\Rememberme\Storage\StorageInterface::TRIPLET_FOUND));
$this->storage->expects($this->once())
->method("replaceTriplet")
->with(
$this->equalTo($this->userid),
$this->matchesRegularExpression('/^[a-f0-9]{32,}'.preg_quote($salt)."$/"),
$this->equalTo($this->validPersistentToken.$salt)
);
$this->rememberme->setSalt($salt);
$this->rememberme->login();
}
public function testSaltIsAddedToTokensOnCookieIsValid() {
$salt = "Mozilla Firefox 4.0";
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("findTriplet")
->with($this->equalTo($this->userid), $this->equalTo($this->validToken.$salt), $this->equalTo($this->validPersistentToken.$salt));
$this->rememberme->setSalt($salt);
$this->rememberme->cookieIsValid($this->userid);
}
public function testSaltIsAddedToTokensOnCreateCookie() {
$salt = "Mozilla Firefox 4.0";
$testExpr = '/^[a-f0-9]{32,}'.preg_quote($salt).'$/';
$this->storage->expects($this->once())
->method("storeTriplet")
->with(
$this->equalTo($this->userid),
$this->matchesRegularExpression($testExpr),
$this->matchesRegularExpression($testExpr)
);
$this->rememberme->setSalt($salt);
$this->rememberme->createCookie($this->userid);
}
public function testSaltIsAddedToTokensOnClearCookie() {
$salt = "Mozilla Firefox 4.0";
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$this->storage->expects($this->once())
->method("cleanTriplet")
->with(
$this->equalTo($this->userid),
$this->equalTo($this->validPersistentToken.$salt)
);
$this->rememberme->setSalt($salt);
$this->rememberme->clearCookie(true);
}
/* Other functions */
public function testCreateCookieCreatesCookieAndStoresTriplets() {
$now = time();
$this->cookie->expects($this->once())
->method("setcookie")
->with(
$this->equalTo($this->rememberme->getCookieName()),
$this->matchesRegularExpression('/^'.$this->userid.'\|[a-f0-9]{32,}\|[a-f0-9]{32,}$/'),
$this->greaterThan($now)
);
$testExpr = '/^[a-f0-9]{32,}$/';
$this->storage->expects($this->once())
->method("storeTriplet")
->with(
$this->equalTo($this->userid),
$this->matchesRegularExpression($testExpr),
$this->matchesRegularExpression($testExpr)
);
$this->rememberme->createCookie($this->userid);
}
public function testClearCookieExpiresCookieAndDeletesTriplet() {
$_COOKIE[$this->rememberme->getCookieName()] = implode("|", array(
$this->userid, $this->validToken, $this->validPersistentToken));
$now = time();
$this->cookie->expects($this->once())
->method("setcookie")
->with(
$this->equalTo($this->rememberme->getCookieName()),
$this->anything(),
$this->lessThan($now)
);
$this->storage->expects($this->once())
->method("cleanTriplet")
->with(
$this->equalTo($this->userid),
$this->equalTo($this->validPersistentToken)
);
$this->rememberme->clearCookie(true);
}
}
+98
View File
@@ -0,0 +1,98 @@
<?php
/*
*/
require_once dirname(__FILE__).'/../bootstrap.php';
require_once "PHPUnit/Extensions/Database/TestCase.php";
/**
* @author birke
*/
class Rememberme_Storage_PDOTest extends PHPUnit_Extensions_Database_TestCase {
/**
*
* @var PDO
*/
protected $pdo;
/**
*
* @var Rememberme_Storage_PDO
*/
protected $storage;
protected $userid = 'test';
protected $validToken = "78b1e6d775cec5260001af137a79dbd5";
protected $validPersistentToken = "0e0530c1430da76495955eb06eb99d95";
protected $invalidToken = "7ae7c7caa0c7b880cb247bb281d527de";
// SHA1 hashes of the tokens
protected $validDBToken = 'e0e6d29addce0fbdd0f845799be7d0395ed087c3';
protected $validDBPersistentToken = 'd27d330764ef61e99adf5d16f90b95a2a63c209a';
protected $invalidDBToken = 'ec15fbc40cdff6a2050a1bcbbc1b2196222f13f4';
protected $expire = "2012-12-21 21:21:00";
protected $expireTS = 1356121260;
protected function getConnection()
{
$this->pdo = new PDO('mysql:host=localhost;dbname=test', 'webuser', '');
return $this->createDefaultDBConnection($this->pdo, 'test');
}
protected function getDataSet()
{
return $this->createFlatXMLDataSet(dirname(__FILE__).'/tokens.xml');
}
protected function setUp() {
parent::setUp();
$this->storage = new Rememberme_Storage_PDO(array(
'connection' => $this->pdo,
'tableName' => 'tokens',
'credentialColumn' => 'credential',
'tokenColumn' => 'token',
'persistentTokenColumn' => 'persistent_token',
'expiresColumn' => 'expires'
));
}
public function testFindTripletReturnsFoundIfDataMatches() {
$result = $this->storage->findTriplet($this->userid, $this->validToken, $this->validPersistentToken);
$this->assertEquals(Rememberme_Storage_StorageInterface::TRIPLET_FOUND, $result);
}
public function testFindTripletReturnsNotFoundIfNoDataMatches() {
$this->pdo->exec("TRUNCATE tokens");
$result = $this->storage->findTriplet($this->userid, $this->validToken, $this->validPersistentToken);
$this->assertEquals(Rememberme_Storage_StorageInterface::TRIPLET_NOT_FOUND, $result);
}
public function testFindTripletReturnsInvalidTokenIfTokenIsInvalid() {
$result = $this->storage->findTriplet($this->userid, $this->invalidToken, $this->validPersistentToken);
$this->assertEquals(Rememberme_Storage_StorageInterface::TRIPLET_INVALID, $result);
}
public function testStoreTripletSavesValuesIntoDatabase() {
$this->pdo->exec("TRUNCATE tokens");
$this->storage->storeTriplet($this->userid, $this->validToken, $this->validPersistentToken, $this->expireTS);
$result = $this->pdo->query("SELECT credential,token,persistent_token, expires FROM tokens");
$row = $result->fetch(PDO::FETCH_NUM);
$this->assertEquals(array($this->userid, $this->validDBToken, $this->validDBPersistentToken, $this->expire), $row);
$this->assertFalse($result->fetch());
}
public function testCleanTripletRemovesEntryFromDatabase() {
$this->storage->cleanTriplet($this->userid, $this->validPersistentToken);
$this->assertEquals(0, $this->pdo->query("SELECT COUNT(*) FROM tokens")->fetchColumn());
}
public function testCleanAllTripletsRemovesAllEntriesWithMatchingCredentialsFromDatabase() {
$this->pdo->exec("INSERT INTO tokens VALUES ('{$this->userid}', 'dummy', 'dummy', NOW())");
$this->storage->cleanAllTriplets($this->userid);
$this->assertEquals(0, $this->pdo->query("SELECT COUNT(*) FROM tokens")->fetchColumn());
}
}
?>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<tokens credential="test"
token="e0e6d29addce0fbdd0f845799be7d0395ed087c3"
persistent_token="d27d330764ef61e99adf5d16f90b95a2a63c209a"
expires="2012-12-21 21:21:00"
/>
</dataset>
+3
View File
@@ -0,0 +1,3 @@
<?php
require_once __DIR__."/../vendor/autoload.php";