mirror of
https://github.com/twisterarmy/skeleton.git
synced 2025-01-08 22:17:56 +00:00
This commit is contained in:
parent
944bb5c3a3
commit
436d71909a
43
public/.htaccess
Normal file
43
public/.htaccess
Normal file
@ -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]
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
4
public/index.php
Normal file
4
public/index.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
require '../vendor/autoload.php';
|
||||
(new \Twister\Container(require '../src/config/container.php'))->execute();
|
99
src/config/container.php
Normal file
99
src/config/container.php
Normal file
@ -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);
|
||||
}
|
||||
];
|
22
src/config/demo.php
Normal file
22
src/config/demo.php
Normal file
@ -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
|
||||
]
|
||||
]);
|
31
src/config/my_server_name.php
Normal file
31
src/config/my_server_name.php
Normal file
@ -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.`
|
||||
]
|
||||
];
|
57
src/config/routes.php
Normal file
57
src/config/routes.php
Normal file
@ -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(); }
|
||||
];
|
14
src/controllers/AdminController.php
Normal file
14
src/controllers/AdminController.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
class AdminController extends Controller
|
||||
{
|
||||
function indexAction()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
function clubAction($id)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
28
src/controllers/LoginController.php
Normal file
28
src/controllers/LoginController.php
Normal file
@ -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();
|
||||
}
|
||||
}
|
17
src/controllers/robots.txt.php
Normal file
17
src/controllers/robots.txt.php
Normal file
@ -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
|
||||
};
|
22
src/controllers/sitemap.xml.php
Normal file
22
src/controllers/sitemap.xml.php
Normal file
@ -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
src/elements/404/init.php
Normal file
0
src/elements/404/init.php
Normal file
6
src/elements/404/view.php
Normal file
6
src/elements/404/view.php
Normal file
@ -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 />
|
5
src/elements/admin/index/init.php
Normal file
5
src/elements/admin/index/init.php
Normal file
@ -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/']);
|
3
src/elements/admin/index/view.php
Normal file
3
src/elements/admin/index/view.php
Normal file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
echo '<div>admin index view</div>';
|
0
src/elements/footer/init.php
Normal file
0
src/elements/footer/init.php
Normal file
0
src/elements/footer/view.php
Normal file
0
src/elements/footer/view.php
Normal file
6
src/elements/header/init.php
Normal file
6
src/elements/header/init.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return function($response)
|
||||
{
|
||||
|
||||
};
|
0
src/elements/header/view.php
Normal file
0
src/elements/header/view.php
Normal file
6
src/elements/index/init.php
Normal file
6
src/elements/index/init.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return function($response)
|
||||
{
|
||||
|
||||
};
|
0
src/elements/index/view.php
Normal file
0
src/elements/index/view.php
Normal file
33
src/elements/login/init.php
Normal file
33
src/elements/login/init.php
Normal file
@ -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();
|
||||
};
|
199
src/elements/login/post.php
Normal file
199
src/elements/login/post.php
Normal file
@ -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);
|
||||
}
|
||||
*/
|
68
src/elements/login/view.php
Normal file
68
src/elements/login/view.php
Normal file
@ -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>
|
60
src/layouts/admin.php
Normal file
60
src/layouts/admin.php
Normal file
@ -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>';
|
||||
};
|
||||
};
|
60
src/layouts/public.php
Normal file
60
src/layouts/public.php
Normal file
@ -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
Block a user