therselman
7 years ago
26 changed files with 783 additions and 0 deletions
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
Order allow,deny |
||||
Allow from all |
||||
Require all granted |
||||
|
||||
SetEnv COMPUTERNAME demo |
||||
|
||||
Options -Indexes |
||||
IndexIgnore */* |
||||
|
||||
php_value error_reporting -1 |
||||
php_flag display_errors On |
||||
php_value max_execution_time 0 |
||||
php_value default_charset "utf-8" |
||||
php_value session.use_trans_sid 0 |
||||
php_value session.gc_maxlifetime 1440 |
||||
php_value session.hash_function 0 |
||||
php_value session.hash_bits_per_character 4 |
||||
php_flag output_buffering Off |
||||
php_flag zlib.output_compression On |
||||
php_flag magic_quotes_gpc Off |
||||
php_flag register_globals Off |
||||
php_value sendmail_from no-reply@demo.com |
||||
php_value memory_limit 64M |
||||
php_value post_max_size 500M |
||||
php_value upload_max_filesize 500M |
||||
|
||||
<Files *.log> |
||||
Order Deny,Allow |
||||
Deny from all |
||||
</Files> |
||||
|
||||
<IfModule mod_headers.c> |
||||
Header unset ETag |
||||
<filesMatch "\.(js|css)$"> |
||||
Header set Cache-Control "max-age=31536000, public" |
||||
</filesMatch> |
||||
</IfModule> |
||||
FileETag None |
||||
|
||||
RewriteEngine On |
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f |
||||
RewriteRule ^ /index.php [L] |
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
<?php |
||||
|
||||
require '../vendor/autoload.php'; |
||||
(new \Twister\Container(require '../src/config/container.php'))->execute(); |
@ -0,0 +1,99 @@
@@ -0,0 +1,99 @@
|
||||
<?php |
||||
|
||||
/** |
||||
* Pre-configured default properties for the master Container |
||||
* This is the default property array of the main Container class. It features some common properties, object instances and factory methods. |
||||
* The Container object itself essentially takes the place of any `global` variables. eg. configuration files, database connection etc. |
||||
* The great thing about this technique is that ALL these objects/properties are lazy loaded! ie. they are not instantiated until we need/call them! |
||||
* This technique is somewhat siliar to this pseudo-code: 'new Request(new DB(new Config()))' where 'config' will be resolved first! |
||||
* Maybe it can also be explained like this: '$c->request($c->db($c->config))' ... but this is just pseudo-code! |
||||
* These are anonymous functions that get called when one of the Container properties are accessed |
||||
* eg. A statement like `$c->db->query(...)`; will call the 'db' function handler code below (when it tries to resolve `$c->db` and automatically builds the `db` instance. |
||||
* The code will create the object instance internally, |
||||
* then assign the new instance value to the corresponding (same) location in the Container class at the end of the function, |
||||
* effectively overwriting/replacing the array member (anonymous function) with the new object instance, |
||||
* essentially building something like a singleton object instance, |
||||
* since calling `$c->db` the next time will return the new object and not the original function. |
||||
* If you require multiple instances, you can just skip assigning the value at the end and just return a new object each time eg. 'return $obj;' instead of 'return $c->obj = $obj;' |
||||
* If you require multiple arguments, like `$c->db('primary_conn')`, or `$c->myFunc($param1)` |
||||
* then you can just create a function like `function($c, $param1)`, |
||||
* the container object itself ($this) is always inserted as the first parameter in the parameter list before the function is called (with array_unshift()). |
||||
* Each one of these `properties` are called by the __get, __set and __call magic methods in the Container class! |
||||
* The `__get` method of the Container is actually called when we ask for '$c->db' (eg. $c->__get('db')) ... but the internal value of 'db' is actually the function below. |
||||
* Our `__get` method inside the Container class will recognize that the property is actually a callable function, |
||||
* and proceed to actually CALL the function, passing it the `$this` (container) value as the first parameter of the function call |
||||
*/ |
||||
return [ |
||||
'execute' => function($c) |
||||
{ |
||||
/** |
||||
* Register an Exception Handler |
||||
*/ |
||||
/** |
||||
* The Twister Exception Handler is just a slightly improved version of the default internal PHP handler |
||||
* new \Twister\ExceptionHandler(); |
||||
* or use Whoops below! |
||||
*/ |
||||
$whoops = new \Whoops\Run; |
||||
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler); |
||||
// $whoops->pushHandler(new \Whoops\Handler\JsonResponseHandler); |
||||
$whoops->register(); |
||||
$c->request->execute_route(); |
||||
}, |
||||
|
||||
'config' => function($c) |
||||
{ |
||||
$config = require __DIR__ . '/' . strtolower(getenv('COMPUTERNAME')) . '.php'; |
||||
$c->is_debug = $c->isDebug = $config['debug']; // shorthand |
||||
return $c->config = &$config; |
||||
}, |
||||
|
||||
'db' => function($c) |
||||
{ |
||||
$dbc = $c->config['db']; |
||||
mysqli_report( MYSQLI_REPORT_ALL ); |
||||
try { |
||||
$db = new DB($dbc['host'], $dbc['username'], $dbc['password'], $dbc['schema'], $dbc['port']); |
||||
} catch (mysqli_sql_exception $e) { |
||||
header('HTTP/1.1 503 Service Temporarily Unavailable'); |
||||
header('Status: 503 Service Temporarily Unavailable'); |
||||
header('Retry-After: 7200'); // seconds |
||||
die('<b>Website under scheduled maintenance!</b><br />' . |
||||
'We are aware of the situation and apologize for the inconvenience!<br />' . |
||||
'Normal operation will resume shortly, please be patient and try again later!<br />' . ($c->is_debug ? mysqli_connect_error() : null)); |
||||
} |
||||
$db->set_charset($dbc['charset']); |
||||
$db->real_query('SET NAMES ' . $dbc['charset'] . ' COLLATE ' . $dbc['collation']); |
||||
return $c->db = &$db; |
||||
}, |
||||
|
||||
/** |
||||
* This Request object will instantiate the 'db' object internally, because the container is Injected into the Request object constructor, |
||||
* it includes the 'db' handler above, so when Request calls '$this->container->db->query(...)'; |
||||
* it will (unknowingly) instantiate the 'db' object when the container tries to resolve the 'db' handler above, |
||||
* the 'db' handler is the function above, which returns the new 'db' object. |
||||
* Another way to do this is use a static variable inside the function, but then we have the unecessary function call overhead. |
||||
* Upon executing any 'db' queries, the 'db' object is automatically instantiated by the 'db' code above. |
||||
* Code like: `$db = new DB(...);` is never actually called anywhere else in code. |
||||
*/ |
||||
'request' => function($c) |
||||
{ |
||||
return $c->request = new Request($c); |
||||
}, |
||||
|
||||
'session' => function($c) |
||||
{ |
||||
return $c->session = new Session($c->db); |
||||
/* // alternative: check if HTTPS is enabled |
||||
if ($c->request->is_https) |
||||
return $c->session = new Session($c->db); |
||||
else |
||||
return $c->session = new Session($c->db); |
||||
*/ |
||||
}, |
||||
|
||||
'user' => function($c) |
||||
{ |
||||
return $c->user = new User($c); |
||||
} |
||||
]; |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
<?php |
||||
|
||||
/** |
||||
* This config file overwrites values in my_server_name.php |
||||
* my_server_name.php is not loaded in code except here |
||||
* We load config files by lowercase(COMPUTERNAME).php |
||||
* The COMPUTERNAME value has been set to 'demo' in .htaccess |
||||
* Change the `my_server_name` name to your server's name |
||||
*/ |
||||
return array_merge(require __DIR__ . '/my_server_name.php', |
||||
[ |
||||
'debug' => true, |
||||
'db' => [ 'host' => '127.0.0.1', |
||||
'username' => 'root', |
||||
'password' => '(-Y${K_c,Hg*3H|E2nu^XT?R3>68!@m:U]5eM&2#', |
||||
'schema' => 'fcm', |
||||
'port' => '33306', |
||||
'charset' => 'utf8mb4', |
||||
'collation' => 'utf8mb4_unicode_ci', |
||||
'debug' => true |
||||
] |
||||
]); |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
<?php |
||||
|
||||
/** |
||||
* This `my_server_name.php` file is not loaded directly in code, except in demo.php |
||||
* 'demo' represents the COMPUTERNAME value of a dev machine |
||||
* So config files are loaded by lowercase(COMPUTERNAME).php, |
||||
* which then loads and overwrites these default values |
||||
* For example, here we set 'debug' to false, but in our 'dev' machines we overwrite it |
||||
* Also, our dev machine(s) can overwrite the database connection values etc. |
||||
*/ |
||||
return [ |
||||
'debug' => false, |
||||
'maintenance' => false, |
||||
'email_from' => 'DEMO <noreply@example.com>', |
||||
'charset' => 'UTF-8', |
||||
'title' => 'Demo Application', |
||||
'lang' => 'en', |
||||
'languages' => ['en'], |
||||
'db' => [ 'host' => '127.0.0.1', |
||||
'username' => 'root', |
||||
'password' => 'XR7mswQKfgsgdfhgregGEzAedxYUDqk6iHBCtI3pcN', |
||||
'schema' => 'demo', |
||||
'port' => '3306', |
||||
'charset' => 'utf8mb4', |
||||
'collation' => 'utf8mb4_unicode_ci' |
||||
], |
||||
'google' => [ 'places' => '34fgdrrdg343t4dfgdfg34tdfsdfghg3q4tghert', // Google Places API server key ... SECRET!!! |
||||
'recaptcha-site' => 'hj345wehjqwereghjj56io54unety354khngdrg3', // `Use this in the HTML code your site serves to users.` |
||||
'recaptcha-secret' => 'hj345wehjqwereghjj56io54unety354khngdrg3' // `Use this for communication between your site and Google. Be sure to keep it a secret.` |
||||
] |
||||
]; |
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
<?php // expressive, fast, flexible
|
||||
|
||||
return [ |
||||
'patterns' => [ // official named patterns |
||||
'alnum' => '[A-Za-z0-9]', |
||||
'word' => '[A-Za-z0-9_]', |
||||
'alpha' => '[A-Za-z]', |
||||
'digit' => '[0-9]', |
||||
'lower' => '[a-z]', |
||||
'xdigit' => '[A-Fa-f0-9]', |
||||
|
||||
// common |
||||
'any' => '[^/]+', |
||||
'string' => '[A-Za-z0-9_-]+', |
||||
'id' => '[0-9]+', |
||||
'int' => '[0-9]+', |
||||
'num' => '[0-9]+', |
||||
'i' => '[0-9]+', |
||||
'd' => '[0-9]', |
||||
'x' => '[A-Fa-f0-9]', |
||||
'l' => '[a-z]', |
||||
'u' => '[A-Z]', |
||||
'U' => '[A-Z]', |
||||
'hex' => '[A-Fa-f0-9]', |
||||
'date' => '\d{4}-\d{2}-\d{2}', |
||||
'day' => '0[1-9]|[12][0-9]|3[01]', |
||||
'month' => '0[1-9]|1[012]', |
||||
'year' => '[12][0-9]{3}', |
||||
'uuid' => '[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}', |
||||
// 'action' => 'index|show|add|create|edit|update|remove|del|delete|view|item', |
||||
], |
||||
|
||||
'routes' => [ '' => [ null, '/', function(Container $c) { (new Response($c, 'public', 'index'))->render(); }, 'home' ], |
||||
'admin' => [ [ 'GET', '', 'AdminController::indexAction', 'admin' ], |
||||
[ 'GET', 'asset-groups', 'AdminController::assetGroupsAction', 'admin_asset_groups' ], |
||||
[ 'POST', 'asset-groups', 'AdminController::assetGroupsPostAction', 'admin_asset_groups_post' ], |
||||
[ 'GET', 'asset-servers', 'AdminController::assetServersAction', 'admin_asset_servers' ], |
||||
[ 'GET', 'assets', 'AdminController::assetsAction', 'admin_assets' ], |
||||
[ 'GET', 'asset/{id}', 'AdminController::assetAction', 'admin_asset' ], |
||||
[ 'GET', 'article/{id}', 'AdminController::articleAction', 'admin_article' ], |
||||
[ 'GET', 'articles', 'AdminController::articlesAction', 'admin_articles' ], |
||||
[ 'GET', 'languages', 'AdminController::languagesAction', 'admin_languages' ], |
||||
[ 'GET', 'club/{id}', 'AdminController::clubAction', 'admin_club' ], |
||||
], |
||||
|
||||
'login' => [ null, null, 'LoginController::loginAction', 'login' ], |
||||
'register' => [ null, null, 'LoginController::registerAction', 'register' ], |
||||
'forgot-password' => [ null, null, 'LoginController::forgotPasswordAction', 'forgot_password' ], |
||||
'resend-verification' => [ null, null, 'LoginController::resendVerificationAction', 'resend_verification' ], |
||||
'logout' => [ null, null, 'LoginController::logoutAction', 'logout' ], |
||||
|
||||
'robots.txt' => 'robots.txt', |
||||
'sitemap.xml' => 'sitemap.xml' |
||||
], |
||||
|
||||
404 => function(Container $c) { (new Response($c, 'public', '404'))->render(); } |
||||
]; |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
<?php |
||||
|
||||
class AdminController extends Controller |
||||
{ |
||||
function indexAction() |
||||
{ |
||||
|
||||
} |
||||
|
||||
function clubAction($id) |
||||
{ |
||||
|
||||
} |
||||
} |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
<?php |
||||
|
||||
/** |
||||
* This login controller does NOT extend the base/abstract Controller class |
||||
* For an example of one that does, look at the AdminController |
||||
* The router builds these action handlers (class methods) parameter lists dynamically |
||||
* So in the first action, I added DB $db (current database connection) (which is not needed inside the function, just an example) |
||||
* And the registerAction() asks for a non-existant parameter |
||||
* These functions can also receive parameters from the $_GET and $_POST arrays. |
||||
* eg. /search?q=term%20term => searchAction($q) |
||||
* value of $q above will be filled with $_GET['q'] value |
||||
*/ |
||||
|
||||
class LoginController |
||||
{ |
||||
static function loginAction(DB $db, Container $c) |
||||
{ |
||||
if ($c->request->isGet()) |
||||
(new Response($c, 'public', 'login'))->render(); |
||||
else |
||||
throw new \Exception('Process login action here ...'); |
||||
} |
||||
|
||||
static function registerAction(Container $c, $non_existing_param1 = null) |
||||
{ |
||||
(new Response($c, 'public', 'register'))->render(); |
||||
} |
||||
} |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
<?php |
||||
|
||||
/** |
||||
* Example |
||||
*/ |
||||
return function( Container $c ) |
||||
{ |
||||
header('Content-Type: text/plain'); |
||||
?> |
||||
User-agent: * |
||||
Allow: / |
||||
Disallow: /admin/ |
||||
|
||||
Sitemap: http://<?php echo $c->request->uri->authority; ?>/sitemap.xml |
||||
|
||||
<?php |
||||
}; |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
<?php |
||||
|
||||
/** |
||||
* Example |
||||
*/ |
||||
return function( Container $c ) |
||||
{ |
||||
header('Content-type: text/xml'); |
||||
?> |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"> |
||||
<url> |
||||
<loc>http://example.com/</loc> |
||||
<lastmod>2006-11-18</lastmod> |
||||
<changefreq>daily</changefreq> |
||||
<priority>0.8</priority> |
||||
</url> |
||||
</urlset> |
||||
<?php |
||||
}; |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
Swigert: "Okay, Houston, we've had a problem here."<br /> |
||||
Lousma: "This is Houston. Say again, please."<br /> |
||||
Lovell: "Uh, Houston, we've had a problem."<br /> |
||||
Lovell: "We've had a <b>404 Page Not Found</b>."<br /> |
||||
Lousma: "Roger, 404 Not Found."<br /> |
||||
Lousma: "Okay, stand by, Thirteen, we're looking at it."<br /> |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
<?php |
||||
|
||||
// if (empty($_SESSION['id'])) redirect(request::build_url(array('scheme' => 'https', 'path' => '/login')), array('message' => 'invalid permissions', 'next' => '/admin/')); |
||||
// if (empty($_SESSION['id'])) redirect(request::build_url('https'), 'invalid-permissions'); |
||||
if (empty($_SESSION['id'])) request::redirect('https', '/login', ['message' => 'invalid permissions', 'next' => '/admin/']); |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
<?php |
||||
|
||||
echo '<div>admin index view</div>'; |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
<?php |
||||
|
||||
return function($response) |
||||
{ |
||||
|
||||
}; |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
<?php |
||||
|
||||
return function($response) |
||||
{ |
||||
|
||||
}; |
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
<?php |
||||
|
||||
return function($response) |
||||
{ |
||||
|
||||
//$user = $response->container->user; |
||||
|
||||
//echo $response->container->request->uri->getLeftPart(Uri::PARTIAL_AUTHORITY) . '/'; |
||||
//echo $response->container->request->uri->getLeftPart(Uri::PARTIAL_PATH) . '/mdfg'; |
||||
|
||||
//echo $response->container->request->uri->withScheme(null); |
||||
|
||||
|
||||
if ( ! $response->container->request->is_https) // || ! isset($_COOKIE[session_name()]) |
||||
$response->container->request->redirect('https'); |
||||
|
||||
$response->scripts['recaptcha'] = 'https://www.google.com/recaptcha/api.js'; |
||||
|
||||
// PROBLEM: if we only create session when the session cookie is set, it's only set (retrieved) on the SECOND page view! |
||||
// What I mean is, if you go immediately to the login page (no other page views), it will NOT have a session cookie stored yet! |
||||
// Actually, the cookie is stored on the client side, but it's not retrieveable yet, until the second page view!?!? |
||||
// Therefore, this session challenge will not be stored in the database if we first check to see if the cookie exists!?!? |
||||
// Maybe we should `fake` it on this page only, by manually setting the session cookie, so when the page ends, and the session must write, |
||||
// it will write the challenge. ie. We need to manually set `$_COOKIE[session_name()] = session_create_id();` |
||||
$_SESSION['challenge'] = md5(uniqid(microtime().mt_rand(), true)); // http://php.net/manual/en/function.mcrypt-create-iv.php |
||||
|
||||
// HACK |
||||
// We might need to do this!?!? |
||||
// What we are doing here is CREATING a cookie that didn't exist before. |
||||
// ALTERNATIVE: We REDIRECT back to this page if the cookie didn't exist before! |
||||
// This `might` be necessary on this page only, to FORCE the session_write_close() function to write the session data (which includes the `challenge`) to the database! |
||||
$_COOKIE[session_name()] = session_id(); |
||||
}; |
@ -0,0 +1,199 @@
@@ -0,0 +1,199 @@
|
||||
<?php |
||||
|
||||
$email = (string) get('email'); |
||||
$password = (string) get('password'); |
||||
$challenge = (string) get('challenge'); |
||||
$next = (string) get('next'); // get('next', '', false); |
||||
$persistent = (bool) get('persistent'); |
||||
$response = (string) get('g-recaptcha-response'); |
||||
|
||||
/* |
||||
// DEBUGGING |
||||
print_pre(''); |
||||
var_dump($_POST); |
||||
print_pre(''); |
||||
var_dump($_SESSION); |
||||
print_pre(''); |
||||
var_dump($_COOKIE); |
||||
print_pre(''); |
||||
var_dump(session_id()); |
||||
print_pre(''); |
||||
var_dump($challenge); |
||||
*/ |
||||
|
||||
$from = '/login'; |
||||
// Used in login & register scripts |
||||
function delay($msg) |
||||
{ |
||||
global $next, $from; |
||||
// unset($_SESSION['challenge']); // should be destroyed with session_destroy() below! |
||||
session_regenerate_id(true); |
||||
// session_write_close(); // was originally using session_regenerate_id(true) + session_write_close() ... changed to session_regenerate_id(true) + session_destroy() |
||||
session_destroy(); |
||||
db::conn()->close(); |
||||
sleep(3); |
||||
redirect($from, array('message' => $msg, 'next' => $next)); |
||||
} |
||||
//print_r($_POST); |
||||
//print_r($_SESSION); |
||||
|
||||
if (empty($_SESSION['challenge']) || $challenge != $_SESSION['challenge']) delay('login-failed1'); |
||||
if (empty($email)) delay('login-failed2'); |
||||
if (empty($password)) delay('login-failed3'); |
||||
if (empty($response)) delay('login-failed4'); |
||||
|
||||
|
||||
// |
||||
// Verify reCAPTCHA |
||||
// |
||||
// https://www.google.com/recaptcha/admin#site/319890180?setup |
||||
// https://developers.google.com/recaptcha/docs/verify |
||||
|
||||
require CODE_PATH . 'lib/curl.php'; |
||||
$curl = new curl(); |
||||
$curl->post('https://www.google.com/recaptcha/api/siteverify', 'secret=' . env::get('google')['recaptcha-secret'] . '&response=' . $response, array(CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_SSL_VERIFYPEER => 0)); |
||||
|
||||
//curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0); // I needed these to pass my self signed certificate. cURL complains about unable to verify local certificate or something! |
||||
//curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0); |
||||
|
||||
if ($curl->response === false && env::isDebug()) |
||||
{ |
||||
var_dump($curl->response); |
||||
var_dump($curl->error()); |
||||
var_dump($curl->errno()); |
||||
var_dump($curl->info); |
||||
} |
||||
|
||||
if ($curl->response === false) |
||||
delay('login-captcha-failed'); |
||||
$response = json_decode($curl->response, true); |
||||
if (!isset($response['success']) || $response['success'] === false) // https://developers.google.com/recaptcha/docs/verify |
||||
delay('login-captcha-failed'); |
||||
|
||||
$user = db::lookup('SELECT id, email_verified, password, salt FROM users WHERE email_hash = 0x' . md5($email) . ' LIMIT 1'); |
||||
if (empty($user) || hash_hmac('sha256', $password, $user['salt'], true) !== $user['password']) |
||||
delay('login-failed6'); |
||||
|
||||
unset($_SESSION['challenge']); |
||||
|
||||
//var_dump($_COOKIE); |
||||
//var_dump($_SESSION); |
||||
|
||||
/* |
||||
// Original code in session class |
||||
static function login($user_id, $persistent) |
||||
{ |
||||
session_regenerate_id(true); |
||||
setcookie(session_name(), session_id(), $persistent ? 0x7fffffff : 0, '/'); |
||||
$_SESSION['id'] = $user_id; |
||||
self::$_db->real_query('INSERT INTO sessions (id, timestamp, persistent, user_id, data) VALUES (0x' . session_id() . ', UNIX_TIMESTAMP(), ' . ($persistent ? 'UNIX_TIMESTAMP(), ' : '0, ') . $user_id . ', "")'); |
||||
} |
||||
|
||||
// call the function |
||||
session::login($user['id'], $persistent); |
||||
*/ |
||||
// We do almost everything here, because I don't want to 'polute' the session class with functionality that's only called/used here! |
||||
session_regenerate_id(true); |
||||
|
||||
//var_dump($_COOKIE); |
||||
//var_dump($_SESSION); |
||||
|
||||
// This cookie is used to `force` (redirect) the browser to the HTTPS url of ALL pages due to a possible session timeout! |
||||
setrawcookie('HTTPS_ONLY', time(), 0x7fffffff, '/'); |
||||
|
||||
setrawcookie(session_name(), session_id(), $persistent ? 0x7fffffff : 0, '/', null, true, true); // $_SERVER["HTTP_HOST"] || null ??? ... This ALSO ensures that the session cookie will ONLY be sent over HTTPS! |
||||
$_SESSION['id'] = $user['id']; |
||||
// session::login($user['id'], $persistent); // This is the only thing we do in the session class, because it requires the session's DB connection ... HOW RETARDED! |
||||
|
||||
//var_dump($_COOKIE); |
||||
//var_dump($_SESSION); |
||||
|
||||
// We MUST lock the `sessions` table, just in-case the session garbage collector runs between the scripts and deletes some extra sessions! |
||||
db::real_query('LOCK TABLES sessions WRITE, user_sessions WRITE, user_session_history WRITE'); |
||||
|
||||
// ARCHIVE USER SESSIONS: `user_sessions` => `user_session_history` |
||||
db::real_query('INSERT INTO user_session_history (id, user_id, ip, cc, agent_id, forwarded_for_id, via_id, created, modified, last_request, requests, page_views, time_on_site, persistent) SELECT id, user_id, ip, cc, agent_id, forwarded_for_id, via_id, created, modified, last_request, requests, page_views, time_on_site, persistent FROM user_sessions WHERE id NOT IN (SELECT id FROM sessions)'); |
||||
db::real_query('DELETE FROM user_sessions WHERE id NOT IN (SELECT id FROM sessions)'); |
||||
|
||||
// Create new session |
||||
if ($persistent) session::create_persistent_session(); // NEW! |
||||
session_write_close(); // might as well do this while we have a table lock !?!? |
||||
|
||||
db::real_query('UNLOCK TABLES'); // I think it's important to unlock these tables early so other scripts can continue to execute !?!? However, this runs so infrequently it shouldn't be an issue!?!? |
||||
|
||||
// Create a new entry in `user_sessions` ... most of the values can be empty, because we will UPDATE them on each page view! |
||||
db::real_query('INSERT INTO user_sessions (id, user_id, ip, cc, agent_id, forwarded_for_id, via_id, created, modified, last_request, requests, page_views, time_on_site, persistent) VALUES (0x' . session_id() . ',' . $user['id'] . ', 0x' . request::$ip2hex . ', "' . request::$cc . '", ' . request::$agent_id . ', ' . request::$forwarded_for_id . ', ' . request::$via_id . ', UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 1, 1, 0, ' . (int) $persistent . ')'); |
||||
|
||||
// ORIGINAL |
||||
/* |
||||
$sql = 'UPDATE users SET ' . |
||||
'previous_online = last_online,' . |
||||
'last_login = NOW(),' . |
||||
'last_online = NOW(),' . |
||||
//'last_domain_id = ' . $GLOBALS['FW']['domain']['id'] . ',' . |
||||
'locale = "' . $GLOBALS['FW']['locale'] . '",' . |
||||
'logins = logins + 1,' . |
||||
'ip = @ip,' . |
||||
'cc = @cc,' . |
||||
'for_id = ' . $logs['forwarder_id'] . ',' . |
||||
'via_id = ' . $logs['proxy_id'] . ',' . |
||||
'agent_id = ' . $logs['agent_id'] . ',' . |
||||
'referer_id = ' . $logs['referer_id'] . ',' . |
||||
'language_id = ' . $logs['language_id'] . |
||||
' WHERE id = ' . $_SESSION['id']; |
||||
*/ |
||||
db::real_query('UPDATE users SET last_login = UNIX_TIMESTAMP(), logins = logins + 1 WHERE id = ' . $user['id']); |
||||
|
||||
// session_write_close(); // moved to the session table lock above, can be moved back without side-effects! |
||||
db::close(); |
||||
|
||||
// ORIGINAL |
||||
// redirect('http://' . $fqdn . $next, array('error' => &$errors, 'warning' => &$warnings, 'message' => &$messages)); |
||||
|
||||
redirect(empty($next) ? '/dashboard/' : $next, array('message' => 'login-welcome')); |
||||
|
||||
|
||||
/* |
||||
// Generate the first password ... |
||||
$salt = microtime(); |
||||
echo md5($salt) . '<br>'; |
||||
echo hash_hmac('sha256', 'abc123', md5($salt, true)) . '<br>'; |
||||
UPDATE users SET email_hash = UNHEX(MD5(email)), password = 0x9f37ce72aaad946e3a98b9dc6126e7c39855600a9d063058dfe79084cbf3a3e4, salt = 0x4c06ff520bc5198942657313a153ec09; |
||||
exit; |
||||
*/ |
||||
|
||||
|
||||
/* |
||||
// ORIGINAL |
||||
$this->update_user_logs(); |
||||
session_id(md5(uniqid(microtime().mt_rand(),true))); |
||||
//setrawcookie(session_name(), session_id(), 0x7fffffff, '/', '.iedb.net', false, true); // problem with this is that it doesn't override the previous session cookie. This is only applicable if we've deleted the cookies and refreshed the post page. |
||||
header('Set-Cookie: PHPSESSID=' . session_id() . ($persistent ? '; expires=Tue, 19-Jan-2038 03:14:07 GMT' : '') . '; path=/; httponly', true); // Later we can modify this to an SSL only cookie! |
||||
$DB->real_query('INSERT INTO sessions (id, timestamp, persistent, user_id, data) VALUES (0x' . session_id() . ', UNIX_TIMESTAMP(), ' . ($persistent ? 'UNIX_TIMESTAMP(), ' : '0, ') . $_SESSION['id'] . ', "")'); |
||||
return true; |
||||
|
||||
function update_user_logs() // try to run this in the framework after we have the $FW['domain']['id'] -- problem is: we don't know if the user has just "logged in" |
||||
{ |
||||
$logs = $this->logs(); |
||||
|
||||
$GLOBALS['DB']->init_ipcc(); |
||||
|
||||
$sql = 'UPDATE users SET ' . |
||||
'previous_online = last_online,' . |
||||
'last_login = NOW(),' . |
||||
'last_online = NOW(),' . |
||||
//'last_domain_id = ' . $GLOBALS['FW']['domain']['id'] . ',' . |
||||
'locale = "' . $GLOBALS['FW']['locale'] . '",' . |
||||
'logins = logins + 1,' . |
||||
'ip = @ip,' . |
||||
'cc = @cc,' . |
||||
'for_id = ' . $logs['forwarder_id'] . ',' . |
||||
'via_id = ' . $logs['proxy_id'] . ',' . |
||||
'agent_id = ' . $logs['agent_id'] . ',' . |
||||
'referer_id = ' . $logs['referer_id'] . ',' . |
||||
'language_id = ' . $logs['language_id'] . |
||||
' WHERE id = ' . $_SESSION['id']; |
||||
|
||||
$GLOBALS['DB']->real_query($sql); |
||||
} |
||||
*/ |
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
|
||||
<div class="panel panel-primary login-panel"> |
||||
<div class="panel-heading">Login</div> |
||||
<div class="panel-body"> |
||||
|
||||
<form id="login" action="<?= request::build_url(['https', 'path' => '/login']) ?>" method="post" onsubmit="return validate_login(this);"> |
||||
<input type="hidden" name="challenge" id="login_challenge" value="<?= $_SESSION['challenge'] ?>" /> |
||||
<input type="hidden" name="next" id="login_next" value="<?= htmlentities(get('next')) ?>" /> |
||||
|
||||
<div class="form-group has-feedback"> |
||||
<input type="text" class="form-control" id="login_email" name="email" placeholder="Email address" /> |
||||
<span class="glyphicon glyphicon-envelope form-control-feedback"></span> |
||||
</div> |
||||
<div class="form-group has-feedback"> |
||||
<input type="password" class="form-control" id="login_password" name="password" placeholder="Password" /> |
||||
<span class="glyphicon glyphicon-lock form-control-feedback"></span> |
||||
</div> |
||||
|
||||
<div class="g-recaptcha" data-sitekey="<?= env::get('google')['recaptcha-site'] ?>"></div> |
||||
|
||||
<br /> |
||||
|
||||
<div class="row"> |
||||
<div class="col-xs-8"> |
||||
<div class="checkbox icheck"> |
||||
<label><input type="checkbox" name="persistent" value="1" /> Keep me logged in</label> |
||||
</div> |
||||
</div> |
||||
<!-- /.col --> |
||||
<div class="col-xs-4"> |
||||
<button type="submit" class="btn btn-primary btn-block btn-flat" id="login_submit">Login</button> |
||||
</div> |
||||
<!-- /.col --> |
||||
</div> |
||||
<span class="ajax-loading hide" id="login_loading"><img src="indicator.gif" alt="Loading" /> Loading...</span> |
||||
</form> |
||||
|
||||
<br /> |
||||
<p>By logging in you are agreeing to our <a>Terms of Use</a> and <a>Privacy Policy</a>.</p> |
||||
|
||||
<hr /> |
||||
<a href="forgot-password">Forgot your password?</a><br /> |
||||
<a href="resend-verification">Resend Email Verification</a><br /> |
||||
|
||||
<hr /> |
||||
<p>Not a member?</p> |
||||
<a href="register" class="btn btn-default btn-block">Create an account</a> |
||||
|
||||
</div> |
||||
</div> |
||||
|
||||
<script type="text/javascript"> |
||||
function validate_login(form) |
||||
{ |
||||
if (form.login_email.value && form.login_password.value) |
||||
{ |
||||
//$("login_loading").className = "ajax-loading"; |
||||
$("login_loading").removeClass("hide"); |
||||
return true; |
||||
} |
||||
if (form.login_email.value == "") |
||||
alert("Please enter your email address!"); |
||||
else |
||||
alert("Please enter your password!"); |
||||
return false; |
||||
} |
||||
|
||||
</script> |
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
<?php |
||||
|
||||
return function($response) |
||||
{ |
||||
$response->title = 'Admin'; |
||||
|
||||
$response->styles['font-awesome'] = 'https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'; // http://fontawesome.io/ |
||||
|
||||
$response->scripts['jquery'] = 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'; |
||||
$response->scripts['bootstrap'] = 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js'; |
||||
|
||||
$response->elements['content'] = null; |
||||
$response->elements['navbar'] = 'admin/navbar'; |
||||
$response->elements['messages'] = 'admin/messages'; |
||||
|
||||
$response->robots = 'noindex,nofollow,noarchive'; |
||||
|
||||
$response->renderer = function() use ($response) |
||||
{ |
||||
echo |
||||
'<!DOCTYPE html>' . PHP_EOL . |
||||
'<html lang="' . $response->lang . '">' . |
||||
'<head id="head>' . |
||||
'<meta charset="utf-8" />' . |
||||
'<meta http-equiv="x-ua-compatible" content="ie=edge" />' . |
||||
'<meta http-equiv="content-type" content="text/html; charset=utf-8" />' . |
||||
'<meta http-equiv="content-language" content="' . $response->lang . '" />' . |
||||
'<title>' . htmlspecialchars($response->title) . '</title>' . |
||||
'<meta name="robots" content="' . $response->robots . '" />' . |
||||
'<link type="image/x-icon" href="/favicon.ico" rel="icon" />' . |
||||
'<link type="image/x-icon" href="/favicon.ico" rel="shortcut icon" />' . |
||||
'<meta name="viewport" content="width=device-width, initial-scale=1" />'; |
||||
foreach ($response->styles as &$url) |
||||
echo '<link rel="stylesheet" type="text/css" href="' . $url . '" />'; |
||||
foreach ($response->scripts as &$url) |
||||
echo '<script type="text/javascript" src="' . $url . '"></script>'; |
||||
echo |
||||
($response->script ? '<script type="text/javascript">' . $response->script . '</script>' : null) . |
||||
'</head>' . |
||||
'<body id="body">' . |
||||
'<div class="container fluid" style="min-height: 600px">'; |
||||
// '<div class="container-fluid">'; |
||||
// '<div class="admin-navbar">'; |
||||
require __DIR__ . '/../elements/' . $response->elements['navbar'] . '/view.php'; |
||||
echo |
||||
// '</div>' . |
||||
'<div class="admin-messages">'; |
||||
require __DIR__ . '/../elements/' . $response->elements['messages'] . '/view.php'; |
||||
echo |
||||
'</div>' . |
||||
'<div class="admin-content">'; |
||||
require __DIR__ . '/../elements/' . $response->elements['content'] . '/view.php'; |
||||
echo |
||||
'</div>' . |
||||
// '</div>' . |
||||
'</div>' . |
||||
'</body>' . |
||||
'</html>'; |
||||
}; |
||||
}; |
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
<?php |
||||
|
||||
/** |
||||
* `layouts` are something like a template pre-processor. |
||||
* Mainly handling common code and default options for all code that use this layout! |
||||
*/ |
||||
return function($response) |
||||
{ |
||||
$response->title = $response->container->config['title']; |
||||
|
||||
$response->styles['font-awesome'] = 'https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'; // http://fontawesome.io/ |
||||
|
||||
$response->scripts['jquery'] = 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'; |
||||
$response->scripts['bootstrap'] = 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js'; |
||||
|
||||
$response->elements['content'] = null; |
||||
$response->elements['header'] = 'header'; |
||||
$response->elements['footer'] = 'footer'; |
||||
|
||||
$response->meta['Play'] = '<link href="https://fonts.googleapis.com/css?family=Play:400,700" rel="stylesheet">'; |
||||
|
||||
$response->renderer = function() use ($response) |
||||
{ |
||||
echo |
||||
'<!DOCTYPE html>' . PHP_EOL . |
||||
'<html lang="' . $response->lang . '">' . |
||||
'<head id="head>' . |
||||
'<meta charset="utf-8" />' . |
||||
'<meta http-equiv="x-ua-compatible" content="ie=edge" />' . |
||||
'<meta http-equiv="content-type" content="text/html; charset=utf-8" />' . |
||||
'<meta http-equiv="content-language" content="' . $response->lang . '" />' . |
||||
'<title>' . htmlspecialchars($response->title) . '</title>' . |
||||
'<meta name="description" content="' . htmlspecialchars($response->description) . '" />' . |
||||
'<meta name="keywords" content="' . htmlspecialchars(implode(',', $response->keywords)) . '" />' . |
||||
'<meta name="robots" content="' . $response->robots . '" />' . |
||||
'<link rel="canonical" href="' . htmlspecialchars($response->canonical) . '" />' . |
||||
'<link type="image/x-icon" href="/favicon.ico" rel="icon" />' . |
||||
'<link type="image/x-icon" href="/favicon.ico" rel="shortcut icon" />' . |
||||
'<meta name="viewport" content="width=device-width, initial-scale=1" />'; |
||||
foreach ($response->styles as &$url) |
||||
echo '<link rel="stylesheet" type="text/css" href="' . $url . '" />'; |
||||
foreach ($response->scripts as &$url) |
||||
echo '<script type="text/javascript" src="' . $url . '"></script>'; |
||||
foreach ($response->meta as &$meta) |
||||
echo $meta; |
||||
echo |
||||
'</head>' . |
||||
'<body>' . |
||||
'<div class="container">'; |
||||
|
||||
require __DIR__ . '/../elements/' . $response->elements['header'] . '/view.php'; |
||||
require __DIR__ . '/../elements/' . $response->elements['content'] . '/view.php'; |
||||
require __DIR__ . '/../elements/' . $response->elements['footer'] . '/view.php'; |
||||
|
||||
echo |
||||
'</div>' . |
||||
'</body>' . |
||||
'</html>'; |
||||
}; |
||||
}; |
Loading…
Reference in new issue