Implement remaining security models #19

Merged
danieljsummers merged 8 commits from security-models into main 2024-04-27 23:14:49 +00:00
8 changed files with 119 additions and 8 deletions
Showing only changes of commit 36373aae01 - Show all commits

View File

@ -17,7 +17,7 @@ _(More environments will be detailed as part of a later release; an nginx revers
## PHP Requirements ## PHP Requirements
This is written to target PHP 8.3, and requires the `curl`, `DOM`, and `SQLite3` modules. _(FrankenPHP contains these modules as part of its build.)_ This is written to target PHP 8.3, and requires the `curl`, `DOM`, and `SQLite3` modules and the `php-cli` feature. _(FrankenPHP contains all these as part of its build.)_
# Setup and Configuration # Setup and Configuration
@ -31,10 +31,10 @@ Within the `/src` directory, there is a file named `user-config.php`. This file
### Security Model ### Security Model
There ~~are~~ will be three supported security models, designed around different ways the software may be deployed. There are three supported security models, designed around different ways the software may be deployed. `SECURITY_MODEL` in `user-config.php` **must be set** to one of these values.
- `Securty::SINGLE_USER` assumes that all connections to the instance are the same person. There is no password required, and no username or e-mail address will be displayed for that user. This is a good setup for a single user on a home intranet. **DO NOT PUT AN INSTANCE WITH THIS CONFIGURATION ON THE PUBLIC INTERNET!** If you do, you deserve what you get. - `Securty::SINGLE_USER` assumes that all connections to the instance are the same person. There is no password required, and no username or e-mail address will be displayed for that user. This is a good setup for a single user on a home intranet. **DO NOT PUT AN INSTANCE WITH THIS CONFIGURATION ON THE PUBLIC INTERNET!** If you do, you deserve what you get.
- `Security::SINGLE_USER_WITH_PASSWORD` _(not yet implemented)_ will be the same as the above, but will require a password. This setup is ideal for intranets where the user does not want any other users ending up marking their feeds as read just by browsing them. - `Security::SINGLE_USER_WITH_PASSWORD` is the same as the above but requires a password. This setup is ideal for intranets where the user does not want any other users ending up marking their feeds as read just by browsing them.
- `Security::MULTI_USER` _(not yet implemented)_ will require a known e-mail address and password be provided to establish the identity of each user. This will be the most appropriate setup for an Internet-facing instance, even if there is only one user. - `Security::MULTI_USER` requires a known e-mail address and password be provided to establish the identity of each user. This is the most appropriate setup for an Internet-facing instance, even if there is only one user.
### Database Name ### Database Name

View File

@ -70,6 +70,10 @@ article {
} }
} }
} }
article.docs {
line-height: 1.4rem;
}
input[type=url], input[type=url],
input[type=text], input[type=text],
input[type=email], input[type=email],
@ -103,3 +107,9 @@ button:hover,
flex-flow: row nowrap; flex-flow: row nowrap;
justify-content: space-evenly; justify-content: space-evenly;
} }
code {
font-size: .9rem;
}
p.back-link {
margin-top: -1rem;
}

15
src/public/docs/index.php Normal file
View File

@ -0,0 +1,15 @@
<?php
include '../../start.php';
$db = Data::getConnection();
Security::verifyUser($db, redirectIfAnonymous: false);
page_head('Documentation'); ?>
<h1>Documentation Home</h1>
<article>
<p><a href=./the-cli>About the CLI</a> provides orientation on Feed Reader Central&rsquo;s command line interface
<p><a href=./security-modes>Configuring Security Modes</a> describes the three security modes and how to manage each
of them
</article><?php
page_foot();
$db->close();

View File

@ -0,0 +1,62 @@
<?php
include '../../start.php';
$db = Data::getConnection();
Security::verifyUser($db, redirectIfAnonymous: false);
page_head('Security Modes | Documentation'); ?>
<h1>Configuring Security Modes</h1>
<p class=back-link><a href=./>&lang;&lang; Documentation Home</a>
<article class=docs>
<h2>Security Modes</h2>
<p><strong>Single-User</strong> mode assumes that every connection to the application is the same person. It is
designed for one person to use on a trusted internal network; under no circumstances should an instance
configured this way be reachable from the Internet. However, it is a low-friction way to keep up with feeds from
multiple devices on a home network.
<p><strong>Single-User with Password</strong> mode operates the same way as Single-User mode does, but the
application will require a password. Depending on the strength of the password, this model may be appropriate
for Internet access, but its intent is more for keeping other internal network users from accessing the site
and reading the items before its intended user is able to do so. The password should be set using the CLI.
<p><strong>Multi-User</strong> mode requires both an e-mail address and password before allowing the user to
proceed. It is the most appropriate configuration for an Internet-facing instance, and it can also be used to
provide access to multiple users on an internal network. Managing users is performed via the CLI.
<h2 id=manage-users>Managing Users in Multi-User Mode</h2>
<p>Users can be added or deleted, and passwords set, using the <code>user</code> CLI utility.<br><br>
<em>(For all the &ldquo;password&rdquo; parameters, if a character in the password conflicts with a shell escape
character, enclose the password in double-quotes for *sh or single-quotes for PowerShell.)</em>
<h3>Add a User</h3>
<p><code>php-cli utils/user.php add-user alice@example.com AlicesSecur3P4ssword</code>
<p>The utility should respond with the e-mail address and password that were added. If a user with that e-mail
address already exists, the utility will not add it again.
<h3>Set a User&rsquo;s Password</h3>
<p><code>php-cli utils/user.php set-password bob@example.com AN3wPassCauseB0bForg0t1t</code>
<h3>Delete a User</h3>
<p><code>php-cli utils/user.php delete-user carol@example.com</code>
<p>The utility will require confirmation that the user and their feeds should be deleted. Any input that starts with
the letter &ldquo;y&rdquo; will confirm, and any other input will cancel the process.
<h2 id=change-to-multi>Changing from Single-User to Multi-User Mode</h2>
<p>In Single-User mode, the application uses a known e-mail address and password to mimic multi-user mode where that
user is always logged on. If you have been using the application this way, and decide that you want to run in
multi-user mode instead, you will need to update <code>SECURITY_MODEL</code> in <code>user-config.php</code> to
<code>Security::MULTI_USER</code>.
<p>The e-mail address used for Single-User mode is not allowed to log on in Multi-User mode. If you want to preserve
the feeds defined by the single user, use the CLI to replace its e-mail address and password.
<p><code>php-cli utils/user.php migrate-single-user dave@example.com Dav3sPas$wort</code>
<p>If, however, you do not wish to maintain the single user&rsquo;s information at all, delete it.
<p><code>php-cli utils/user.php remove-single-user</code>
<h2 id=change-multi-to-single>Changing from Multi-User to any Single-User Mode</h2>
<p>This scenario is possible, but not really advisable. When the application is in any Single-User mode, it only
displays feeds from the Single-User mode user. The information for the other users remains in the database,
though, so this change is not destructive.
<h2 id=change-single-to-pw>Changing from Single-User to Single-User with Password Mode</h2>
<p>Set <code>SECURITY_MODEL</code> in <code>user-config.php</code> to
<code>Security::SINGLE_USER_WITH_PASSWORD</code>, then use the <code>user</code> CLI utility to set a password.
<p><code>php-cli util/user.php set-single-password aNiceC0mplexPassw0rd</code>
<h2 id=change-pw-to-single>Changing from Single-User with Password to Single-User Mode</h2>
<p>If you decide you do not want to enter a password, but want to maintain single-user mode, set
<code>SECURITY_MODEL</code> in <code>user-config.php</code> to <code>Security::SINGLE_USER</code>, then run the
<code>user</code> CLI utility to reset the single user back to its expected default.
<p><code>php-cli util/user.php reset-single-password</code>
</article><?php
page_foot();
$db->close();

View File

@ -0,0 +1,24 @@
<?php
include '../../start.php';
$db = Data::getConnection();
Security::verifyUser($db, redirectIfAnonymous: false);
page_head('About the CLI | Documentation'); ?>
<h1>About the CLI</h1>
<p class=back-link><a href=./>&lang;&lang; Documentation Home</a>
<article class=docs>
<p>Feed Reader Central&rsquo;s low-friction design includes having many administrative tasks run in a terminal or
shell. &ldquo;CLI&rdquo; is short for &ldquo;Command Line Interface&rdquo;, and refers to commands that are run
via PHP&rsquo;s <code>php-cli</code> command. Ensure that the version of PHP installed also has that feature
enabled. (If you are using the recommended
<a href=https://frankenphp.dev target=_blank rel=noopener>FrankenPHP</a> environment, it has PHP CLI support;
use <code>frankenphp php-cli</code> instead of <code>php-cli</code> when following instructions here.)
<h2>Running CLI Commands</h2>
<p>CLI commands should be run from the same directory with <code>start.php</code>; this will be one directory level
up from <code>/public</code>, the web root directory. CLI utilities are in the <code>/util</code> directory, so
an invocation will follow the pattern:
<p><code>php-cli util/some-process.php command option1 option2</code>
</article><?php
page_foot();
$db->close();

View File

@ -8,7 +8,7 @@ Security::verifyUser($db, redirectIfAnonymous: false);
if (array_key_exists(Key::USER_ID, $_SESSION)) frc_redirect('/'); if (array_key_exists(Key::USER_ID, $_SESSION)) frc_redirect('/');
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
Security::logOnUser($_POST['email'] ?? '', $_POST['password'], $_POST['returnTo'], $db); Security::logOnUser($_POST['email'] ?? '', $_POST['password'], $_POST['returnTo'] ?? null, $db);
// If we're still here, something didn't work; preserve the returnTo parameter // If we're still here, something didn't work; preserve the returnTo parameter
$_GET['returnTo'] = $_POST['returnTo']; $_GET['returnTo'] = $_POST['returnTo'];
} }

View File

@ -60,10 +60,10 @@ function page_head(string $title): void {
<div><a class=title href="/">Feed Reader Central</a><span class=version>v<?=$version?></span></div> <div><a class=title href="/">Feed Reader Central</a><span class=version>v<?=$version?></span></div>
<div><?php <div><?php
if (array_key_exists(Key::USER_ID, $_SESSION)) { if (array_key_exists(Key::USER_ID, $_SESSION)) {
echo '<a href=/feed?id=new>Add Feed</a> | <a href=/user/log-off>Log Off</a>'; echo '<a href=/feed?id=new>Add Feed</a> | <a href=/docs/>Docs</a> | <a href=/user/log-off>Log Off</a>';
if ($_SESSION[Key::USER_EMAIL] != Security::SINGLE_USER_EMAIL) echo " | {$_SESSION[Key::USER_EMAIL]}"; if ($_SESSION[Key::USER_EMAIL] != Security::SINGLE_USER_EMAIL) echo " | {$_SESSION[Key::USER_EMAIL]}";
} else { } else {
echo '<a href=/user/log-on>Log On</a>'; echo '<a href=/user/log-on>Log On</a> | <a href=/docs/>Docs</a>';
} ?> } ?>
</div> </div>
</header> </header>

View File

@ -12,7 +12,7 @@
* - Security::SINGLE_USER_WITH_PASSWORD (no e-mail required, does require a password) * - Security::SINGLE_USER_WITH_PASSWORD (no e-mail required, does require a password)
* - Security::MULTI_USER (e-mail and password required for all users) * - Security::MULTI_USER (e-mail and password required for all users)
*/ */
const SECURITY_MODEL = Security::SINGLE_USER; const SECURITY_MODEL = 'CONFIGURE_ME';
/** The name of the database file where users and feeds should be kept */ /** The name of the database file where users and feeds should be kept */
const DATABASE_NAME = 'frc.db'; const DATABASE_NAME = 'frc.db';