diff --git a/.docker/init.sh b/.docker/init.sh index 9aa3952..33b6aff 100644 --- a/.docker/init.sh +++ b/.docker/init.sh @@ -38,28 +38,28 @@ if [ "$ULOGGER_DB_DRIVER" = "pgsql" ]; then su postgres -c "psql -c \"GRANT ALL PRIVILEGES ON DATABASE ulogger TO ulogger\"" su postgres -c "psql -d ulogger -c \"GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ulogger\"" su postgres -c "psql -d ulogger -c \"GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ulogger\"" - su postgres -c "psql -d ulogger -c \"INSERT INTO users (login, password) VALUES ('admin', '\\\$2y\\\$10\\\$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq')\"" + su postgres -c "psql -d ulogger -c \"INSERT INTO users (login, password, admin) VALUES ('${ULOGGER_ADMIN_USER}', '\\\$2y\\\$10\\\$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq', TRUE)\"" su postgres -c "pg_ctl -w stop" sed -i "s/^\$dbdsn = .*$/\$dbdsn = \"pgsql:host=localhost;port=5432;dbname=ulogger\";/" /var/www/html/config.php elif [ "$ULOGGER_DB_DRIVER" = "sqlite" ]; then mkdir -p /data/sqlite chown -R nobody:nobody /data sqlite3 -init /var/www/html/scripts/ulogger.sqlite /data/sqlite/ulogger.db .exit - sqlite3 -line /data/ulogger.db "INSERT INTO users (login, password) VALUES ('admin', '\$2y\$10\$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq')" + sqlite3 -line /data/ulogger.db "INSERT INTO users (login, password, admin) VALUES ('${ULOGGER_ADMIN_USER}', '\$2y\$10\$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq', 1)" sed -i "s/^\$dbdsn = .*$/\$dbdsn = \"sqlite:\/data\/sqlite\/ulogger.db\";/" /var/www/html/config.php else mkdir -p /run/mysqld chown mysql:mysql /run/mysqld mysql_install_db --user=mysql --datadir=/data - mysqld_safe --datadir=/data & + mysqld_safe --datadir=/data & mysqladmin --silent --wait=30 ping mysqladmin -u root password "${DB_ROOT_PASS}" - mysql -u root -p"${DB_ROOT_PASS}" < /var/www/html/scripts/ulogger.sql + mysql -u root -p"${DB_ROOT_PASS}" < /var/www/html/scripts/ulogger.mysql mysql -u root -p"${DB_ROOT_PASS}" -e "CREATE USER 'ulogger'@'localhost' IDENTIFIED BY '${DB_USER_PASS}'" mysql -u root -p"${DB_ROOT_PASS}" -e "GRANT ALL PRIVILEGES ON ulogger.* TO 'ulogger'@'localhost'" mysql -u root -p"${DB_ROOT_PASS}" -e "CREATE USER 'ulogger'@'%' IDENTIFIED BY '${DB_USER_PASS}'" mysql -u root -p"${DB_ROOT_PASS}" -e "GRANT ALL PRIVILEGES ON ulogger.* TO 'ulogger'@'%'" - mysql -u root -p"${DB_ROOT_PASS}" -e "INSERT INTO users (login, password) VALUES ('admin', '\$2y\$10\$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq')" ulogger + mysql -u root -p"${DB_ROOT_PASS}" -e "INSERT INTO users (login, password, admin) VALUES ('${ULOGGER_ADMIN_USER}', '\$2y\$10\$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq', TRUE)" ulogger mysqladmin -u root -p"${DB_ROOT_PASS}" shutdown sed -i "s/^\$dbdsn = .*$/\$dbdsn = \"mysql:host=localhost;port=3306;dbname=ulogger;charset=utf8\";/" /var/www/html/config.php fi diff --git a/.docker/run.sh b/.docker/run.sh index 12807fb..3b79acc 100644 --- a/.docker/run.sh +++ b/.docker/run.sh @@ -1,7 +1,6 @@ #!/bin/sh # set config variables -sed -i "s/^\$admin_user = .*$/\$admin_user = \"${ULOGGER_ADMIN_USER}\";/" /var/www/html/config.php sed -i "s/^\$pass_strength = .*$/\$pass_strength = ${ULOGGER_PASS_STRENGTH};/" /var/www/html/config.php sed -i "s/^\$pass_lenmin = .*$/\$pass_lenmin = ${ULOGGER_PASS_LENMIN};/" /var/www/html/config.php sed -i "s/^\$require_authentication = .*$/\$require_authentication = ${ULOGGER_REQUIRE_AUTHENTICATION};/" /var/www/html/config.php diff --git a/.tests/fixtures/fixture_admin.xml b/.tests/fixtures/fixture_admin.xml index 2abb77c..c34a854 100644 --- a/.tests/fixtures/fixture_admin.xml +++ b/.tests/fixtures/fixture_admin.xml @@ -1,6 +1,6 @@ - + diff --git a/.tests/lib/BaseDatabaseTestCase.php b/.tests/lib/BaseDatabaseTestCase.php index 2e71e53..c347ccd 100644 --- a/.tests/lib/BaseDatabaseTestCase.php +++ b/.tests/lib/BaseDatabaseTestCase.php @@ -1,5 +1,4 @@ query("ALTER SEQUENCE users_id_seq RESTART WITH $users"); - self::$pdo->query("ALTER SEQUENCE tracks_id_seq RESTART WITH $tracks"); - self::$pdo->query("ALTER SEQUENCE positions_id_seq RESTART WITH $positions"); - } else if (self::$driver == "sqlite") { + if (self::$driver === "pgsql") { + self::$pdo->exec("ALTER SEQUENCE users_id_seq RESTART WITH $users"); + self::$pdo->exec("ALTER SEQUENCE tracks_id_seq RESTART WITH $tracks"); + self::$pdo->exec("ALTER SEQUENCE positions_id_seq RESTART WITH $positions"); + } else if (self::$driver === "sqlite") { $retry = 1; do { try { - self::$pdo->query("DELETE FROM sqlite_sequence WHERE NAME = 'users'"); - self::$pdo->query("DELETE FROM sqlite_sequence WHERE NAME = 'tracks'"); - self::$pdo->query("DELETE FROM sqlite_sequence WHERE NAME = 'positions'"); + self::$pdo->exec("DELETE FROM sqlite_sequence WHERE NAME = 'users'"); + self::$pdo->exec("DELETE FROM sqlite_sequence WHERE NAME = 'tracks'"); + self::$pdo->exec("DELETE FROM sqlite_sequence WHERE NAME = 'positions'"); $retry = 0; } catch (Exception $e) { // sqlite raises error when db schema changes in another connection. @@ -175,12 +174,13 @@ abstract class BaseDatabaseTestCase extends PHPUnit_Extensions_Database_TestCase * * @param string $user User login * @param string $pass User password + * @param bool $isAdmin User is admin * @return int|bool User id or false on error */ - protected function addTestUser($user = NULL, $pass = NULL) { + protected function addTestUser($user = NULL, $pass = NULL, $isAdmin = false) { if (is_null($user)) { $user = $this->testUser; } if (is_null($pass)) { $pass = $this->testPass; } - $id = $this->pdoInsert('users', [ 'login' => $user, 'password' => $pass ]); + $id = $this->pdoInsert('users', [ 'login' => $user, 'password' => $pass, 'admin' => (int) $isAdmin ]); if ($id !== false) { return (int) $id; } diff --git a/.tests/tests/AuthTest.php b/.tests/tests/AuthTest.php index 3620bdf..a96085b 100644 --- a/.tests/tests/AuthTest.php +++ b/.tests/tests/AuthTest.php @@ -1,5 +1,4 @@ checkLogin($this->testUser, $this->testPass); $this->assertTrue($auth->isAuthenticated(), "Not authenticated"); - $this->assertTrue($auth->user instanceof uUser, "User variable not set"); + $this->assertInstanceOf(uUser::class, $auth->user, "User variable not set"); $this->assertEquals($this->testUser, $auth->user->login, "Wrong login"); $this->assertEquals($_SESSION["user"]->login, $auth->user->login, "Wrong login"); - $this->assertTrue($_SESSION["user"] instanceof uUser, "User not set in session"); + $this->assertInstanceOf(uUser::class, $_SESSION["user"], "User not set in session"); } /** @@ -38,7 +37,7 @@ class AuthTest extends UloggerDatabaseTestCase { $auth = new uAuth(); $auth->checkLogin($this->testUser, "badPass"); $this->assertFalse($auth->isAuthenticated(), "Should not be authenticated"); - $this->assertTrue(is_null($auth->user), "User not null"); + $this->assertInternalType('null', $auth->user, "User not null"); } /** @@ -51,7 +50,7 @@ class AuthTest extends UloggerDatabaseTestCase { $auth = new uAuth(); $auth->checkLogin("", $this->testPass); $this->assertFalse($auth->isAuthenticated(), "Should not be authenticated"); - $this->assertTrue(is_null($auth->user), "User not null"); + $this->assertInternalType('null', $auth->user, "User not null"); } /** @@ -63,7 +62,7 @@ class AuthTest extends UloggerDatabaseTestCase { $auth = new uAuth(); $this->assertFalse($auth->isAuthenticated(), "Should not be authenticated"); - $this->assertTrue(is_null($auth->user), "User not null"); + $this->assertInternalType('null', $auth->user, "User not null"); } /** @@ -109,7 +108,7 @@ class AuthTest extends UloggerDatabaseTestCase { /** * @runInSeparateProcess */ - public function testNotIsAdmin() { + public function testIsNotAdmin() { $this->addTestUser($this->testUser, password_hash($this->testPass, PASSWORD_DEFAULT)); $this->assertEquals(1, $this->getConnection()->getRowCount('users'), "Wrong row count"); @@ -123,15 +122,13 @@ class AuthTest extends UloggerDatabaseTestCase { * @runInSeparateProcess */ public function testIsAdmin() { - $this->addTestUser($this->testUser, password_hash($this->testPass, PASSWORD_DEFAULT)); + $this->addTestUser($this->testUser, password_hash($this->testPass, PASSWORD_DEFAULT), true); $this->assertEquals(1, $this->getConnection()->getRowCount('users'), "Wrong row count"); - uConfig::$admin_user = $this->testUser; - @$auth = new uAuth(); $auth->checkLogin($this->testUser, $this->testPass); $this->assertTrue($auth->isAuthenticated(), "Should be authenticated"); - $this->assertTrue($auth->isAdmin(), "Should not be admin"); + $this->assertTrue($auth->isAdmin(), "Should be admin"); } } diff --git a/.tests/tests/UserTest.php b/.tests/tests/UserTest.php index 3ba4af5..872f441 100644 --- a/.tests/tests/UserTest.php +++ b/.tests/tests/UserTest.php @@ -1,5 +1,4 @@ assertEquals(2, $this->getConnection()->getRowCount('users'), "Wrong row count"); $userArr = uUser::getAll(); - $this->assertEquals(2, count($userArr), "Wrong array size"); - $this->assertTrue($userArr[0] instanceof uUser, "Wrong array member"); + $this->assertCount(2, $userArr, "Wrong array size"); + $this->assertInstanceOf(uUser::class, $userArr[0], "Wrong array member"); } public function testIsAdmin() { + $this->addTestUser($this->testUser, NULL, true); + $user = new uUser($this->testUser); + $this->assertTrue($user->isAdmin, "User should be admin"); + } + + public function testIsNotAdmin() { $this->addTestUser($this->testUser); $user = new uUser($this->testUser); $this->assertFalse($user->isAdmin, "User should not be admin"); - uConfig::$admin_user = $this->testUser; - $user = new uUser($this->testUser); - $this->assertTrue($user->isAdmin, "User should be admin"); } public function testIsValid() { diff --git a/README.md b/README.md index 88ac199..e1e14a9 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,13 @@ Together with a dedicated [μlogger mobile client](https://github.com/bfabiszews - You may also want to set your new user as an [admin in config file](https://github.com/bfabiszewski/ulogger-server/blob/v0.2/config.default.php#L67). - Folders `.docker/` and `.tests/` as well as composer files are needed only for development. May be safely removed. +## Upgrade to version 1.x +- TODO: convert following notes to migration script +- Database changes: + - `ALTER TABLE positions CHANGE image_id image VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL` + - `ALTER TABLE users ADD admin BOOLEAN NOT NULL DEFAULT FALSE AFTER password` + - modify admin user entry in `users` table: set `admin` to `true` + ## Docker - Run `docker run --name ulogger -p 8080:80 -d bfabiszewski/ulogger` and access `http://localhost:8080` in your browser. Log in with `admin`:`admin` credentials and change default password. - Optional configuration options with ENV variables, for list see [Dockerfile](https://github.com/bfabiszewski/ulogger-server/blob/master/Dockerfile). The variables correspond to main μlogger configuration parameteres. diff --git a/config.default.php b/config.default.php index 325c7f0..62427a1 100644 --- a/config.default.php +++ b/config.default.php @@ -55,13 +55,6 @@ $require_authentication = 1; // (0 = no, 1 = yes) $public_tracks = 0; -// admin user, who -// - can add new users -// - can edit all tracks, users -// - has access to all users locations -// none if empty -$admin_user = ""; - // miniumum required length of user password $pass_lenmin = 12; diff --git a/helpers/config.php b/helpers/config.php index 58733d2..8f4015b 100644 --- a/helpers/config.php +++ b/helpers/config.php @@ -30,69 +30,69 @@ /** * @var string Version number */ - static $version = "1.0-beta"; + public static $version = "1.0-beta"; /** * @var string Default map drawing framework */ - static $mapapi = "openlayers"; + public static $mapapi = "openlayers"; /** * @var string|null Google maps key */ - static $gkey = null; + public static $gkey; /** * @var array Openlayers additional map layers */ - static $ol_layers = []; + public static $ol_layers = []; /** * @var float Default latitude for initial map */ - static $init_latitude = 52.23; + public static $init_latitude = 52.23; /** * @var float Default longitude for initial map */ - static $init_longitude = 21.01; + public static $init_longitude = 21.01; /** * @var string Database dsn */ - static $dbdsn = ""; + public static $dbdsn = ""; /** * @var string Database user */ - static $dbuser = ""; + public static $dbuser = ""; /** * @var string Database pass */ - static $dbpass = ""; + public static $dbpass = ""; /** * @var string Optional table names prefix, eg. "ulogger_" */ - static $dbprefix = ""; + public static $dbprefix = ""; /** * @var bool Require login/password authentication */ - static $require_authentication = true; + public static $require_authentication = true; /** * @var bool All users tracks are visible to authenticated user */ - static $public_tracks = false; + public static $public_tracks = false; /** * @var string Admin user who has access to all users locations * none if empty */ - static $admin_user = ""; + public static $admin_user = ""; /** * @var int Miniumum required length of user password */ - static $pass_lenmin = 12; + public static $pass_lenmin = 12; /** * @var int Required strength of user password @@ -101,35 +101,35 @@ * 2 = require mixed case and numbers * 3 = require mixed case, numbers and non-alphanumeric characters */ - static $pass_strength = 2; + public static $pass_strength = 2; /** * @var int Default interval in seconds for live auto reload */ - static $interval = 10; + public static $interval = 10; /** * @var string Default language code */ - static $lang = "en"; + public static $lang = "en"; /** * @var string Default units */ - static $units = "metric"; + public static $units = "metric"; /** * @var int Stroke weight */ - static $strokeWeight = 2; + public static $strokeWeight = 2; /** * @var string Stroke color */ - static $strokeColor = '#ff0000'; + public static $strokeColor = '#ff0000'; /** * @var int Stroke opacity */ - static $strokeOpacity = 1; + public static $strokeOpacity = 1; private static $fileLoaded = false; @@ -138,7 +138,7 @@ /** * Static initializer */ - static public function init() { + public static function init() { if (!self::$initialized) { self::setFromFile(); self::setFromCookies(); diff --git a/helpers/user.php b/helpers/user.php index 7d44d71..5aae21c 100644 --- a/helpers/user.php +++ b/helpers/user.php @@ -42,15 +42,15 @@ public function __construct($login = NULL) { if (!empty($login)) { try { - $query = "SELECT id, login, password FROM " . self::db()->table('users') . " WHERE login = ? LIMIT 1"; + $query = "SELECT id, login, password, admin FROM " . self::db()->table('users') . " WHERE login = ? LIMIT 1"; $stmt = self::db()->prepare($query); $stmt->execute([ $login ]); $stmt->bindColumn('id', $this->id, PDO::PARAM_INT); $stmt->bindColumn('login', $this->login); $stmt->bindColumn('password', $this->hash); + $stmt->bindColumn('admin', $this->isAdmin, PDO::PARAM_BOOL); if ($stmt->fetch(PDO::FETCH_BOUND)) { $this->isValid = true; - $this->isAdmin = self::isAdmin($this->login); } } catch (PDOException $e) { // TODO: handle exception @@ -68,22 +68,23 @@ return uDb::getInstance(); } - /** - * Add new user - * - * @param string $login Login - * @param string $pass Password - * @return int|bool New user id, false on error - */ - public static function add($login, $pass) { + /** + * Add new user + * + * @param string $login Login + * @param string $pass Password + * @param bool $isAdmin Is admin + * @return int|bool New user id, false on error + */ + public static function add($login, $pass, $isAdmin = false) { $userid = false; if (!empty($login) && !empty($pass) && self::validPassStrength($pass)) { $hash = password_hash($pass, PASSWORD_DEFAULT); $table = self::db()->table('users'); try { - $query = "INSERT INTO $table (login, password) VALUES (?, ?)"; + $query = "INSERT INTO $table (login, password, admin) VALUES (?, ?, ?)"; $stmt = self::db()->prepare($query); - $stmt->execute([ $login, $hash ]); + $stmt->execute([ $login, $hash, (int) $isAdmin ]); $userid = (int) self::db()->lastInsertId("${table}_id_seq"); } catch (PDOException $e) { // TODO: handle exception @@ -199,7 +200,7 @@ */ public static function getAll() { try { - $query = "SELECT id, login, password FROM " . self::db()->table('users') . " ORDER BY login"; + $query = "SELECT id, login, password, admin FROM " . self::db()->table('users') . " ORDER BY login"; $result = self::db()->query($query); $userArr = []; while ($row = $result->fetch()) { @@ -224,19 +225,9 @@ $user->id = $row['id']; $user->login = $row['login']; $user->hash = $row['password']; - $user->isAdmin = self::isAdmin($row['login']); + $user->isAdmin = (bool) $row['admin']; $user->isValid = true; return $user; } - - /** - * Is given login admin user - * - * @param string $login Login - * @return bool True if admin, false otherwise - */ - private static function isAdmin($login) { - return (!empty(uConfig::$admin_user) && uConfig::$admin_user == $login); - } } ?> diff --git a/scripts/setup.php b/scripts/setup.php index 6ca89a3..ad2063f 100644 --- a/scripts/setup.php +++ b/scripts/setup.php @@ -24,6 +24,7 @@ $enabled = false; /* -------------------------------------------- */ /* no user modifications should be needed below */ +/** @noinspection ConstantCanBeUsedInspection */ if (version_compare(PHP_VERSION, "5.4.0", "<")) { die("Sorry, ulogger will not work with PHP version lower than 5.4 (you have " . PHP_VERSION . ")"); } @@ -63,7 +64,7 @@ switch ($command) { $queries = getQueries($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)); $pdo->beginTransaction(); foreach ($queries as $query) { - $pdo->query($query); + $pdo->exec($query); } $pdo->commit(); } catch (PDOException $e) { @@ -90,7 +91,7 @@ switch ($command) { $login = uUtils::postString("login"); $pass = uUtils::postPass("pass"); - if (uUser::add($login, $pass) !== false) { + if (uUser::add($login, $pass, true) !== false) { $messages[] = "{$langSetup["congratulations"]}"; $messages[] = $langSetup["setupcomplete"]; $messages[] = "{$langSetup["disablewarn"]}
"; @@ -123,7 +124,7 @@ switch ($command) { $messages[] = "
"; break; } - if (ini_get("session.auto_start") == "1") { + if (ini_get("session.auto_start") === "1") { $messages[] = sprintf($langSetup["optionwarn"], "session.auto_start", "0 (off)"); $messages[] = $langSetup["dorestart"]; $messages[] = "
"; @@ -182,7 +183,8 @@ function getQueries($dbDriver) { $queries[] = "CREATE TABLE `$tUsers` ( `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `login` varchar(15) CHARACTER SET latin1 NOT NULL UNIQUE, - `password` varchar(255) CHARACTER SET latin1 NOT NULL DEFAULT '' + `password` varchar(255) CHARACTER SET latin1 NOT NULL DEFAULT '', + `admin` boolean NOT NULL DEFAULT FALSE ) ENGINE=InnoDB DEFAULT CHARSET=utf8"; @@ -224,7 +226,8 @@ function getQueries($dbDriver) { $queries[] = "CREATE TABLE $tUsers ( id SERIAL PRIMARY KEY, login VARCHAR(15) NOT NULL UNIQUE, - password VARCHAR(255) NOT NULL DEFAULT '' + password VARCHAR(255) NOT NULL DEFAULT '', + admin BOOLEAN NOT NULL DEFAULT FALSE )"; $queries[] = "CREATE TABLE $tTracks ( @@ -265,7 +268,8 @@ function getQueries($dbDriver) { $queries[] = "CREATE TABLE `$tUsers` ( `id` integer PRIMARY KEY AUTOINCREMENT, `login` varchar(15) NOT NULL UNIQUE, - `password` varchar(255) NOT NULL DEFAULT '' + `password` varchar(255) NOT NULL DEFAULT '', + `admin` integer NOT NULL DEFAULT 0 )"; $queries[] = "CREATE TABLE `$tTracks` ( `id` integer PRIMARY KEY AUTOINCREMENT, @@ -309,8 +313,7 @@ function getQueries($dbDriver) { */ function getPdo() { $options = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]; - $pdo = new PDO(uConfig::$dbdsn, uConfig::$dbuser, uConfig::$dbpass, $options); - return $pdo; + return new PDO(uConfig::$dbdsn, uConfig::$dbuser, uConfig::$dbpass, $options); } ?> @@ -355,6 +358,7 @@ function getPdo() { color: #00e700; } +