diff --git a/Dockerfile-php b/Dockerfile-php new file mode 100644 index 00000000..fed9ae3a --- /dev/null +++ b/Dockerfile-php @@ -0,0 +1,3 @@ +FROM php:7-fpm + +RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli diff --git a/application/bootstrap.php b/application/bootstrap.php index 1f3dc57d..b9ad7fc9 100644 --- a/application/bootstrap.php +++ b/application/bootstrap.php @@ -110,9 +110,21 @@ * - boolean caching enable or disable internal caching FALSE * - boolean expose set the X-Powered-By header FALSE */ -Kohana::init([ +$_settings = [ 'base_url' => '/', -]); + 'errors' => FALSE, +]; +// Load the Profiler class when profiling is enabled +// otherwise I get the following error: +/* + Fatal error: Uncaught Error: Class 'Profiler' not found in /app/system/classes/Kohana/Core.php:598 Stack trace: #0 /app/system/classes/Kohana/Core.php(427): Kohana_Core::find_file('classes', 'Kohana/Profiler') #1 [internal function]: Kohana_Core::auto_load('Kohana_Profiler') #2 /app/system/classes/Profiler.php(3): spl_autoload_call('Kohana_Profiler') #3 /app/system/classes/Kohana/Core.php(430): require('/app/system/cla...') #4 [internal function]: Kohana_Core::auto_load('Profiler') #5 /app/system/classes/Kohana/Request/Client/Internal.php(60): spl_autoload_call('Profiler') #6 /app/system/classes/Kohana/Request/Client.php(114): Kohana_Request_Client_Internal->execute_request(Object(Request), Object(Response)) #7 /app/system/classes/Kohana/Request.php(1000): Kohana_Request_Client->execute(Object(Request)) #8 /app/index.php(118): Kohana_Request->execute() #9 {main} thrown in /app/system/classes/Kohana/Core.php on line 598 + */ +if (!isset($_settings['profile']) || !empty($_settings['profile'])) +{ + require SYSPATH.'classes/Kohana/Profiler'.EXT; + require SYSPATH.'classes/Profiler'.EXT; +} +Kohana::init($_settings); /** * Attach the file write to logging. Multiple writers are supported. @@ -129,13 +141,13 @@ */ Kohana::modules([ // 'encrypt' => MODPATH.'encrypt', // Encryption supprt - // 'auth' => MODPATH.'auth', // Basic authentication + 'auth' => MODPATH.'auth', // Basic authentication // 'cache' => MODPATH.'cache', // Caching with multiple backends // 'codebench' => MODPATH.'codebench', // Benchmarking tool - // 'database' => MODPATH.'database', // Database access + 'database' => MODPATH.'database', // Database access // 'image' => MODPATH.'image', // Image manipulation // 'minion' => MODPATH.'minion', // CLI Tasks - // 'orm' => MODPATH.'orm', // Object Relationship Mapping + 'orm' => MODPATH.'orm', // Object Relationship Mapping // 'pagination' => MODPATH.'pagination', // Pagination // 'unittest' => MODPATH.'unittest', // Unit testing // 'userguide' => MODPATH.'userguide', // User guide and API documentation @@ -148,7 +160,7 @@ * If you have not defined a cookie salt in your Cookie class then * uncomment the line below and define a preferrably long salt. */ -// Cookie::$salt = NULL; +Cookie::$salt = '2139821639u2yhrd3912u'; /** * Cookie HttpOnly directive * If set to true, disallows cookies to be accessed from JavaScript diff --git a/application/classes/Controller/Welcome.php b/application/classes/Controller/Welcome.php index 58dcd4de..ae7b7585 100644 --- a/application/classes/Controller/Welcome.php +++ b/application/classes/Controller/Welcome.php @@ -2,9 +2,60 @@ class Controller_Welcome extends Controller { + public function action_login() + { + $status = Auth::instance()->login('newadmin' ,'somepass', true); + if ($status) { + $this->redirect('welcome/index'); + } else { + $this->response->body('login failed!'); + } + } + + public function action_logout() + { + Auth::instance()->logout(TRUE); + $this->redirect('welcome/index'); + } + public function action_index() { - $this->response->body('hello, world!'); + $user = null; + try { + $user = Auth::instance()->get_user(); + } catch (Exception $e) { + echo $e."
"; + } + if (!$user) { + $this->response->body('hello, anon user!
login
register user'); + } else { + $this->response->body('hello, ' . $user->username . '.
logout'); + } + } + + public function action_register() + { + $user = ORM::factory('User'); + $user->where('username', '=', 'newadmin')->find(); + if (!$user->loaded()) { + $user = ORM::factory('user'); + $user->username = 'newadmin'; + $user->password = 'somepass'; + $user->email = 'foor@bar.org'; + try{ + $user->save(); + $user->add('roles', ORM::factory('Role')->where('name', '=', 'login')->find()); + $user->save(); + $this->response->body('user created'); + } + catch(ORM_Validation_Exception $e){ + var_dump($e->errors()); + die(); + } + } else { + $this->response->body('user exists'); + + } } } // End Welcome diff --git a/application/config/auth.php b/application/config/auth.php new file mode 100755 index 00000000..5b286746 --- /dev/null +++ b/application/config/auth.php @@ -0,0 +1,12 @@ + 'ORM', + 'hash_method' => 'sha256', + 'hash_key' => '123598123568612638', + 'lifetime' => 1209600, + 'session_type' => Session::$default, // native + 'session_key' => 'auth_user', + 'users' => [] +); diff --git a/application/config/database.php b/application/config/database.php new file mode 100755 index 00000000..5cee94fc --- /dev/null +++ b/application/config/database.php @@ -0,0 +1,53 @@ + array + ( + 'type' => 'MySQLi', + 'connection' => array( + /** + * The following options are available for MySQL: + * + * string hostname + * string username + * string password + * boolean persistent + * string database + * + * Ports and sockets may be appended to the hostname. + */ + 'hostname' => 'mysql', + 'username' => 'root', + 'password' => 'somepass', + 'persistent' => FALSE, + 'database' => 'koseven', + ), + 'table_prefix' => '', + 'charset' => 'utf8', + 'caching' => FALSE, + 'profiling' => TRUE, + ), + 'alternate' => array( + 'type' => 'pdo', + 'connection' => array( + /** + * The following options are available for PDO: + * + * string dsn + * string username + * string password + * boolean persistent + * string identifier + */ + 'dsn' => 'mysql:host=mysql;dbname=koseven', + 'username' => 'root', + 'password' => 'somepass', + 'persistent' => FALSE, + ), + 'table_prefix' => '', + 'charset' => 'utf8', + 'caching' => FALSE, + 'profiling' => TRUE, + ), +); diff --git a/application/config/url.php b/application/config/url.php new file mode 100644 index 00000000..bd8628e3 --- /dev/null +++ b/application/config/url.php @@ -0,0 +1,8 @@ + [ + 'localhost:8080', + ], +]; diff --git a/db.sql b/db.sql new file mode 100644 index 00000000..9f3e990b --- /dev/null +++ b/db.sql @@ -0,0 +1,53 @@ +CREATE DATABASE IF NOT EXISTS `koseven` DEFAULT CHARACTER SET utf8 COLLATE utf8_polish_ci; +USE `koseven`; + +CREATE TABLE IF NOT EXISTS `roles` ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `name` varchar(32) NOT NULL, + `description` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO `roles` (`id`, `name`, `description`) VALUES(1, 'login', 'Login privileges, granted after account confirmation'); +INSERT INTO `roles` (`id`, `name`, `description`) VALUES(2, 'admin', 'Administrative user, has access to everything.'); + +CREATE TABLE IF NOT EXISTS `roles_users` ( + `user_id` int(10) UNSIGNED NOT NULL, + `role_id` int(10) UNSIGNED NOT NULL, + PRIMARY KEY (`user_id`,`role_id`), + KEY `fk_role_id` (`role_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `users` ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `email` varchar(254) NOT NULL, + `username` varchar(32) NOT NULL DEFAULT '', + `password` varchar(64) NOT NULL, + `logins` int(10) UNSIGNED NOT NULL DEFAULT '0', + `last_login` int(10) UNSIGNED, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_username` (`username`), + UNIQUE KEY `uniq_email` (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `user_tokens` ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` int(11) UNSIGNED NOT NULL, + `user_agent` varchar(40) NOT NULL, + `token` varchar(40) NOT NULL, + `created` int(10) UNSIGNED NOT NULL, + `expires` int(10) UNSIGNED NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_token` (`token`), + KEY `fk_user_id` (`user_id`), + KEY `expires` (`expires`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `roles_users` + ADD CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE; + +ALTER TABLE `user_tokens` + ADD CONSTRAINT `user_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE; + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..7663aa84 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +version: '2' + +services: + mysql: + image: mysql:5.6.45 + ports: + - "3306:3306" + volumes: + - ./db.sql:/docker-entrypoint-initdb.d/dump.sql + environment: + - MYSQL_ROOT_PASSWORD=somepass + web: + image: nginx:latest + ports: + - "8080:80" + volumes: + - .:/app + - ./site.conf:/etc/nginx/conf.d/default.conf:ro + php: + image: custom-php + build: + context: . + dockerfile: Dockerfile-php + volumes: + - .:/app diff --git a/index.php b/index.php new file mode 100644 index 00000000..f302ee11 --- /dev/null +++ b/index.php @@ -0,0 +1,121 @@ +execute(); +} +else +{ + /** + * Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO']. + * If no source is specified, the URI will be automatically detected. + */ + echo Request::factory(TRUE, [], FALSE) + ->execute() + ->send_headers(TRUE) + ->body(); +} diff --git a/modules/orm/classes/Kohana/ORM.php b/modules/orm/classes/Kohana/ORM.php index c1ebd6d1..eb6efca9 100644 --- a/modules/orm/classes/Kohana/ORM.php +++ b/modules/orm/classes/Kohana/ORM.php @@ -573,6 +573,9 @@ public function serialize() // Store only information about the object foreach (['_primary_key_value', '_object', '_changed', '_loaded', '_saved', '_sorting', '_original_values'] as $var) { + // this causes (de)serialization issues when an object is used in both _object and _original_values vars + // it this case if an object is shared between those two, it will be serialized in _original_values as a reference + // and cause deserialization issues.php. $data[$var] = serialize($this->{$var}) would be a dirty workaround here. $data[$var] = $this->{$var}; } diff --git a/modules/orm/classes/Model/Auth/User.php b/modules/orm/classes/Model/Auth/User.php index c192b859..67bf52d7 100644 --- a/modules/orm/classes/Model/Auth/User.php +++ b/modules/orm/classes/Model/Auth/User.php @@ -92,6 +92,10 @@ public function complete_login() // Save the user $this->update(); + + // reload the user so the logins is not referencing the Database_Expression anymore + // otherwise it will be serialized to a json in a session and lead to errors + $this->reload(); } } diff --git a/site.conf b/site.conf new file mode 100644 index 00000000..a465e7be --- /dev/null +++ b/site.conf @@ -0,0 +1,26 @@ +server { + index index.php index.html; + server_name localhost; + error_log /dev/stdout info; + access_log /dev/stdout; + root /app; + index index.php; + + rewrite_log on; + + + location / { + expires off; + try_files $uri $uri/ /index.php?$uri&$args; + } + + location ~ .*\.(php|php5)?$ { + try_files $uri =404; + fastcgi_pass php:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + } + +}