Index: /trunk/bin/_config.inc.php
===================================================================
--- /trunk/bin/_config.inc.php	(revision 673)
+++ /trunk/bin/_config.inc.php	(revision 674)
@@ -39,5 +39,4 @@
 define('CODEBASE_PATH', realpath(dirname(__FILE__) . '/../'));
 
-
 $db_auth_file = false;
 if (!defined('_NODB')) {
@@ -51,13 +50,19 @@
     }
     if (!$db_auth_file) {
-        die(sprintf("%s error: the current directory must be common site directory (i.e. the parent directory of the document root) AND a db_auth file must exist within two directory levels.\n", $cli_executed));
+        printf("%s error: the current directory must be common site directory (i.e. the parent directory of the document root) AND a db_auth file must exist within two directory levels.\n", $cli_executed);
+        exit(1);
     }
     if (fileowner($db_auth_file) != getmyuid()) {
-        die(sprintf("%s error: you must execute this script as the owner of the web files.\n", $cli_executed));
+        printf("%s error: you must execute this script as the owner of the web files.\n", $cli_executed);
+        exit(1);
     }
 }
 
 // Set include path.
-ini_set('include_path', get_include_path() . PATH_SEPARATOR . COMMON_BASE);
+ini_set('include_path', join(PATH_SEPARATOR, array_unique(array(
+    '.',
+    COMMON_BASE,
+    dirname($db_auth_file)
+))));
 
 /********************************************************************
@@ -66,6 +71,5 @@
 
 // Include core libraries.
-require_once CODEBASE_PATH . '/lib/App.inc.php';
-require_once CODEBASE_PATH . '/lib/Utilities.inc.php';
+(include_once 'codebase/lib/App.inc.php') || require_once CODEBASE_PATH . '/lib/App.inc.php';
 
 define('_CLI', true);
Index: /trunk/bin/acl.cli.php
===================================================================
--- /trunk/bin/acl.cli.php	(revision 673)
+++ /trunk/bin/acl.cli.php	(revision 674)
@@ -189,8 +189,6 @@
 Access Control List command line tool.
 
-This script must be run in the common site directory (i.e. the parent
-directory of the document root). DB credentials are retrieved from:
-global/db_auth.inc.php so this file must exist. Furthermore this script
-must be executed as the owner of the db_auth.inc.php file.
+This script must be run in a common site directory configured with a DB auth file,
+e.g., `lib/db_auth.json`, readable by the user executing this script.
 
 Three types of objects are managed by this interface:
Index: /trunk/bin/user.cli.php
===================================================================
--- /trunk/bin/user.cli.php	(revision 674)
+++ /trunk/bin/user.cli.php	(revision 674)
@@ -0,0 +1,243 @@
+#!/usr/bin/env php
+<?php
+/**
+ * The Strangecode Codebase - a general application development framework for PHP
+ * For details visit the project site: <http://trac.strangecode.com/codebase/>
+ * Copyright 2001-2012 Strangecode, LLC
+ *
+ * This file is part of The Strangecode Codebase.
+ *
+ * The Strangecode Codebase is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * The Strangecode Codebase is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+* user.cli.php
+*
+* @author   Quinn Comendant <quinn@strangecode.com>
+* @version  1.0
+* @since    02 May 2019 14:21:12
+*/
+
+/********************************************************************
+* CONFIG
+********************************************************************/
+
+// Find a _config.inc.php file and load it.
+$_config_file = false;
+$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.'));
+$rii->setMaxDepth(1);
+foreach ($rii as $filename => $file) {
+    if (mb_strpos($filename, '/_config.inc.php') !== false) {
+        $_config_file = $filename;
+        break;
+    }
+}
+if (!$_config_file) {
+    echo "Error: could not find _config.inc.php in current directory or subdirectories.\n";
+    exit(1);
+}
+define('_CLI', true);
+require_once $_config_file;
+
+
+/********************************************************************
+* MAIN
+********************************************************************/
+
+if (isset($CFG) && is_object($CFG)) {
+    echo "user-add.cli.php is not compatible with codebase v1.*\n";
+    exit(1);
+}
+
+if (!isset($db) || !is_a($db, 'DB')) {
+    echo "This project doesn't have a \$db object.\n";
+    exit(1);
+}
+
+if (!isset($auth) || !is_a($auth, 'Auth_SQL')) {
+    echo "This project doesn't have an \$auth object.\n";
+    exit(1);
+}
+
+if (!$auth->getParam('db_table') ||
+    !$auth->getParam('db_primary_key') ||
+    !$auth->getParam('db_username_column')) {
+    echo "This project's \$auth object does not have the required db_* parameters.\n";
+    exit(1);
+}
+
+// COMMAND
+$command = User_CLI::getArg(1, 'command');
+switch ($command) {
+case 'help':
+    User_CLI::usage();
+    exit(1);
+
+case 'list':
+    $users = User_CLI::getList();
+    $positions = "%-3s  %-15s  %-11s  %-15s\n";
+    printf($positions,
+        'ID',
+        'USERNAME',
+        'LAST_ACCESS',
+        'LAST_IP'
+    );
+    foreach ($users as $u) {
+        printf($positions,
+            $u[$auth->getParam('db_primary_key')],
+            $u['username'],
+            date($app->getParam('date_format'), strtotime($u['last_access_datetime'])),
+            $u['last_login_ip']
+        );
+    }
+    break;
+
+case 'create':
+    $username = User_CLI::getArg(2, 'username');
+    $password = User_CLI::getArg(3, 'password');
+    $user_id = $auth->getUserID($username);
+    if (false !== $auth->getUserID($username)) {
+        printf("User '%s' already exists. Use 'update' instead.\n", $username);
+        exit(1);
+    }
+    $user_id = User_CLI::create($username, $password);
+    printf("Created user '%s' (user_id %s).\n", $username, $user_id);
+    break;
+
+case 'update':
+    $username = User_CLI::getArg(2, 'username');
+    $password = User_CLI::getArg(3, 'password');
+    if (!$user_id = $auth->getUserID($username)) {
+        printf("User '%s' not found. Use 'create' first.\n", $username);
+        exit(1);
+    }
+    $auth->setPassword($user_id, $password);
+    printf("Updated user '%s' password (user_id %s).\n", $username, $user_id);
+    break;
+
+case 'remove':
+    $username = User_CLI::getArg(2, 'username');
+    if (!$user_id = $auth->getUserID($username)) {
+        printf("User '%s' not found.\n", $username);
+        exit(1);
+    }
+    User_CLI::remove($username);
+    printf("Removed user '%s' (user_id %s).\n", $username, $user_id);
+    break;
+
+default:
+    printf("Unknown command: %s\n", $command);
+    break;
+}
+
+// End of script.
+exit(0);
+
+
+/********************************************************************
+* FUNCTIONS
+********************************************************************/
+
+/*
+* Static methods for this script only.
+*/
+class User_CLI
+{
+    static public function getArg($pos, $name)
+    {
+        if (!isset($_SERVER['argv'][$pos]) || $_SERVER['argv'][$pos] == '') {
+            // Required arguments missing.
+            printf("Required argument %s is missing. Lost? Try \`%s help\`.\n", strtoupper($name), basename($_SERVER['argv'][0]));
+            exit(1);
+        }
+
+        return $_SERVER['argv'][$pos];
+    }
+
+    static public function getList()
+    {
+        global $auth, $db;
+
+        $qid = $db->query("
+            SELECT *
+            FROM " . $auth->getParam('db_table') . "
+            LIMIT 1000
+        ");
+        $results = array();
+        while ($row = mysql_fetch_assoc($qid)) {
+            $results[] = $row;
+        }
+
+        return $results;
+    }
+
+    static public function create($username, $password)
+    {
+        global $auth, $db;
+
+        $qid = $db->query("DESCRIBE " . $auth->getParam('db_table'));
+        $cols = array();
+        while ($row = mysql_fetch_row($qid)) {
+            $cols[] = $row[0];
+        }
+        $addtl_cols = array();
+        $addtl_vals = array();
+        if (in_array('account_id', $cols)) {
+            $addtl_cols[] = ", account_id";
+            $addtl_vals[] = ", '1'";
+        }
+        $db->query("
+            INSERT INTO " . $auth->getParam('db_table') . " (
+                " . $auth->getParam('db_primary_key') . ",
+                " . $auth->getParam('db_username_column') . join("\n", $addtl_cols) . "
+            ) VALUES (
+                NULL,
+                '" . $db->escapeString($username) . "'" . join("\n", $addtl_vals) . "
+            )
+        ");
+        $user_id = mysql_insert_id($db->getDBH());
+        $auth->setPassword($user_id, $password);
+
+        return $user_id;
+    }
+
+    static public function remove($username)
+    {
+        global $auth, $db;
+
+        $qid = $db->query("
+            DELETE FROM " . $auth->getParam('db_table') . "
+            WHERE " . $auth->getParam('db_username_column') . " = '" . $db->escapeString($username) . "'
+        ");
+    }
+
+    static public function usage()
+    {
+        ?>
+Manage codebase (Auth_SQL) user accounts.
+
+Usage: <?php echo basename($_SERVER['argv'][0]); ?> COMMAND [âŠ]
+
+COMMANDS
+
+    help                        Display this help
+    list                        List all users.
+    create USERNAME PASSWORD    Create a user USERNAME authenticated by PASSWORD.
+    update USERNAME PASSWORD    Update the password for user USERNAME to PASSWORD.
+
+This script must be run in a common site directory configured with a DB auth file,
+e.g., `lib/db_auth.json`, readable by the user executing this script.
+<?php
+    }
+}
Index: /trunk/lib/Auth_SQL.inc.php
===================================================================
--- /trunk/lib/Auth_SQL.inc.php	(revision 673)
+++ /trunk/lib/Auth_SQL.inc.php	(revision 674)
@@ -896,4 +896,28 @@
     }
 
+    /**
+     * Returns a user_id for a specified username.
+     *
+     * @param  string  $username    Username to look for.
+     * @return string               User_id, or false if none found.
+     */
+    public function getUserID($username)
+    {
+        $db =& DB::getInstance();
+
+        $this->initDB();
+
+        $qid = $db->query("
+            SELECT " . $this->_params['db_primary_key'] . "
+            FROM " . $this->_params['db_table'] . "
+            WHERE " . $this->_params['db_username_column'] . " = '" . $db->escapeString($username) . "'
+        ");
+        if (list($user_id) = mysql_fetch_row($qid)) {
+            return $user_id;
+        } else {
+            return false;
+        }
+    }
+
     /*
     * Generate a cryptographically secure, random password.
