<p><a href="admin_files">Manage (downloadable) files</a></p>
- <p><a href="admin_admins">Manage administrators (passwords, etc.)</a></p>
+ <p><a href="admin_users">Manage accounts (admin passwords, etc.)</a></p>
<p><a href="logout">Log out</a></p>
<!--~}~-->
<?php
-require_once(DOCROOT . 'inc/wfpl/session.php');
-
function admin_main() {
- if(!logged_in_as_admin()) {
- $_REQUEST['url'] = this_url();
- return 'admin_login';
- }
+ session_auth_must('admin_control_panel');
}
+++ /dev/null
-<!DOCTYPE html>
-
-<html>
-<head>
- <meta charset="utf-8" />
- <title><!--~$title show {~-->Accounts<!--~}~--></title>
- <link rel="stylesheet" href="style.css" type="text/css">
-</head>
-
-<body>
-<!--~$body show {~-->
-
- <!--~form {~-->
- <h2><!--~new_msg {~-->Add a new account<!--~}~--><!--~edit_msg {~-->Edit account "~username html~"<!--~}~--></h2>
-
- <form action="~$basename~" method="post"><!--~editing {~--><div style="display: none"><input type="hidden" name="edit_id" value="~id attr~"></div><!--~}~-->
-
- <div class="caption">Name (optional)</div>
- <div class="field"><input type="text" name="name" value="~name attr~"></div>
-
- <div class="caption">Username (required)</div>
- <div class="field_notes">This is used to log in, and is case sensitive, so you may want to stick with all lowercase</div>
- <div class="field"><input type="text" name="username" value="~username attr~"></div>
-
- <div class="caption">Password (case sensitive)</div>
- <div class="field_notes">If this is blank, the user will be unable to log in.</div>
- <!--~editing {~--><div class="field_notes">Below you'll see only the encrypted version of the password. This is the only thing that's stored on the server, so if somebody has forgotten their password, the only thing that can be done about it is setting a new password using this field. If you do not edit this field, their password is unchanged.</div><!--~}~-->
- <div class="field"><input type="text" name="password" value="~password attr~"></div>
-
- <div class="caption">Role</div>
- <div class="field_notes">Set to "None" to disable the account. This is useful if you might want to enable it again with the same password, since (unlike deleting the account) the password is preserved in the database.</div>
- <div class="field"><select name="privs"><!--~privs options~--></select></div>
-
- <div class="caption"> </div>
- <div class="field"><input type="submit" name="save" value="Save"></div>
-
- </form>
-
- <div class="caption"> </div>
- <div class="field"><!--~id {~--><a href="admin_admins?id=~id~">Cancel</a><!--~}~--><!--~id unset {~--><a href="admin_admins">Cancel</a><!--~}~--></div>
- <!--~}~-->
-
- <!--~listings once {~-->
- <h2>Accounts Listing</h2>
-
- <!--~listings once_if {~-->
- <p><a href="admin_admins?new=1">[Add a new account]</a></p>
-
- <table cellspacing="0" cellpadding="4" border="0" summary="" class="evenodd">
- <tr><th>Name</th><th>Username</th><th>Role</th><th> </th></tr><!--~listings {~-->
- <tr>
- <td class="listing"><a href="admin_admins?edit_id=~id~">~name html~<!--~name empty {~--><em>(blank)</em><!--~}~--></a></td>
- <td class="listing"><a href="admin_admins?edit_id=~id~">~username html~<!--~username empty {~--><em>(blank)</em><!--~}~--></a></td>
- <td class="listing"><a href="admin_admins?edit_id=~id~">~privs html~<!--~privs empty {~--><em>(blank)</em><!--~}~--></a></td>
- <td><a href="admin_admins?admin_admins_delete_id=~id~" onclick="return confirm('Permanently delete?')">[delete this account]</a></td>
- </tr><!--~}~-->
-
- </table>
- <!--~}~-->
- <!--~listings once_else {~-->
- <p>No accounts in database.</p>
- <!--~}~-->
-
- <p><a href="admin_admins?new=1">[Add a new account]</a></p>
- <!--~}~-->
-
-<!--~}~-->
-</body>
-</html>
+++ /dev/null
-<?php
-
-# Reset password from the commandline: echo -E "update admins set password="$(echo '<?php print(sha1("NEW_PASSWORD"));' | php)" where username='USERNAME';" | mysql DB_NAME_HERE
-
-define('ADMIN_ADMINS_DB_FIELDS', 'name,username,password,privs');
-
-
-require_once(DOCROOT . 'inc/wfpl/format.php');
-require_once(DOCROOT . 'inc/wfpl/email.php');
-
-function admin_admins_get_fields() {
- $data = array();
-
- $data['name'] = format_oneline(_REQUEST_cut('name'));
- $data['username'] = format_oneline(_REQUEST_cut('username'));
- $data['password'] = format_oneline(_REQUEST_cut('password'));
- if($data['password'] && strlen($data['password']) != 40) {
- $data['password'] = sha1($data['password']);
- }
- $data['privs'] = format_options(_REQUEST_cut('privs'), 'privs');
-
- return $data;
-}
-
-
-function admin_admins_main() {
- if(logged_in_as_admin()) {
- tem_set('admin_privs');
- } else {
- $_REQUEST['url'] = this_url();
- return 'admin_login';
- }
-
- $id = _REQUEST_cut('edit_id');
- if($id) {
- return admin_admins_main_form($id);
- }
-
- $id = _REQUEST_cut('admin_admins_delete_id');
- if($id) {
- return admin_admins_main_delete($id);
- }
-
- if(_REQUEST_cut('new')) {
- return admin_admins_main_form();
- }
-
- if(_REQUEST_cut('list')) {
- return admin_admins_main_listing();
- }
-
- if(isset($_POST['username'])) {
- return admin_admins_main_form();
- }
-
- # default action:
- return admin_admins_main_listing();
-}
-
-function admin_admins_main_delete($id) {
- db_delete('admins', 'where id=%i', $id);
- message('Account deleted.');
- return './admin_admins';
-}
-
-function admin_admins_main_listing() {
- $listing_rows = db_get_assocs('admins', 'id,name,username,privs', 'order by coalesce(nullif("",name),username)');
- tem_set('listings', $listing_rows);
-}
-
-function admin_admins_main_form($id = false) {
- pulldown('privs', array(
- array('', 'None'),
- array('admin', 'Admin')
- ));
-
- if($id) {
- # add hidden field for database id of row we're editing
- tem_set('id', $id);
- tem_set('editing');
- tem_set('edit_msg');
- } else {
- tem_set('new_msg');
- }
-
- if(isset($_POST['username'])) {
- $data = admin_admins_get_fields();
-
- if($data['username']) {
- if($id) {
- db_update_assoc('admins', $data, 'where id=%i', $id);
- message('Account updated.');
- } else {
- db_insert_assoc('admins', $data);
- message('Account saved.');
- }
- if($error !== true) {
- return './admin_admins';
- }
- } else {
- message('"username" is required. To disable an account without deleting it, make the password blank');
- }
- } elseif($id) {
- # we've recieved an edit id, but no data. So we grab the values to be edited from the database
- $data = db_get_assoc('admins', ADMIN_ADMINS_DB_FIELDS, 'where id=%i', $id);
- } else {
- # form not submitted, you can set default values:
- $data = array(
- 'password' => session_generate_key() # [a-zA-Z0-9]{16}
- );
- }
-
- tem_set('form', $data);
-}
+++ /dev/null
-drop table if exists admins;
-create table admins (
- id int unique auto_increment,
- name varchar(100) not null default "",
- username varchar(50) not null default "",
- password varchar(50) not null default "",
- privs varchar(100) not null default ""
-);
-insert into admins (username,password,privs) values (
- 'fixme',
- '98fd71615b073b75810f4ed40d4538198c6450cc', /* sha1("fixme") */
- 'admin');
function admin_files_main() {
- if(logged_in_as_admin()) {
- tem_set('admin_privs');
- } else {
- $_REQUEST['url'] = this_url();
- return 'admin_login';
- }
+ session_auth_must('manage_files');
$id = _REQUEST_cut('edit_id');
if($id) {
function admin_images_main() {
- if(!logged_in_as_admin()) {
- $_REQUEST['url'] = this_url();
- return 'admin_login';
- }
+ session_auth_must('admin_images');
$id = _REQUEST_cut('edit_id');
if($id) {
+++ /dev/null
-<!DOCTYPE html>
-
-<html>
-<head>
- <title><!--~$title show {~-->~$host~ Admin Login<!--~}~--></title>
-</head>
-
-<body>
- <!--~$body show {~-->
- <!--~form {~-->
- <h2>~$host~ Admin Login</h2>
-
- <form action="admin_login" method="post">
- <div class="caption">Username (case sensitive)</div>
- <div class="field"><input type="text" name="username" value="~username attr~" autofocus></div>
-
- <div class="caption">Password (case sensitive)</div>
- <div class="field"><input type="password" name="password" value=""></div>
-
- <div class="caption"> </div>
- <div class="field"><input type="hidden" name="url" value="~url attr~"><input type="submit" value="Log in"></div>
- </form>
- <!--~}~-->
- <!--~}~-->
-</body>
-</html>
+++ /dev/null
-<?php
-
-# This form requires wfpl. See: http://sametwice.com/wfpl
-
-function admin_login_get_fields() {
- $data = array();
-
- $data['url'] = format_oneline($_REQUEST['url']);
- $data['username'] = format_oneline($_REQUEST['username']);
- $data['password'] = sha1(format_oneline($_REQUEST['password']));
-
- return $data;
-}
-
-
-function admin_login_main() {
- # Always accept "url" parameter, so might as well just:
- $data = admin_login_get_fields();
-
- if(strlen($data['username'])) {
- $row = db_get_assoc('admins', 'privs', 'where username=%" && password=%"', $data['username'], $data['password']);
- if($row) {
- session_new();
- session_set('auth_username', $data['username']);
- session_set('auth_' . $row['privs'], 'yes');
- if(!$data['url']) {
- if ($row['privs'] == 'admin') {
- $data['url'] = './admin';
- } else {
- $data['url'] = './';
- }
- } elseif(strpos(':', $data['url']) !== false) {
- $data['url'] = "./$data[url]";
- }
-
- # redirect to the page they were trying to access:
- return $data['url'];
- } else {
- message('Incorrect username and/or password.');
- }
- }
-
- # make sure the hashed password doesn't make it back to the front end
- $data['password'] = '';
-
- # display the form [again]
- tem_set('form', $data);
-}
function admin_pages_main() {
- if(!logged_in_as_admin()) {
- $_REQUEST['url'] = this_url();
- return 'admin_login';
- }
+ session_auth_must('edit_page');
$id = _REQUEST_cut('edit_id');
if($id) {
require_once(DOCROOT . 'inc/wfpl/format.php');
require_once(DOCROOT . 'inc/wfpl/db.php');
require_once(DOCROOT . 'inc/wfpl/session_messages.php');
+require_once(DOCROOT . 'inc/session_auth.php');
require_once(DOCROOT . 'inc/cms.php');
# Connect to the database
$cms_page_id = cms_display_content($tem, 'where filename=%"', $basename);
- if(logged_in_as_admin()) {
+ if(session_auth_can('admin_links')) {
$admin_links = array();
if($cms_page_id) {
$admin_links['id'] = $cms_page_id;
--- /dev/null
+<?php
+
+# normalize usernames (for case-insensitive etc. logins)
+function format_auth_username($str) {
+ $str = iconv('utf8', 'ascii//TRANSLIT', $str);
+ $str = strtolower(trim($str));
+ $str = preg_replace('/[^a-z0-9]/', '', $str);
+ return $str;
+}
+
+# Called automatically by session_auth().
+# Only call if you've just verified that someone has logged in, or has clicked
+# a valid password reset link.
+function session_auth_init($id = false, $password_reset = false) {
+ $GLOBALS['wfpl_session_auth'] = [
+ 'id' => null,
+ 'role' => null,
+ 'name' => null,
+ 'username' => null,
+ 'last_active' => null,
+ 'password_reset' => null
+ ];
+
+ if ($id) {
+ $user = db_get_assoc('users', 'role,name,username', 'where id=%i', $id);
+ $now = time();
+ db_update('users', 'last_active', $now, 'where id=%i', $id);
+ $GLOBALS['wfpl_session_auth']['id'] = $id;
+ $GLOBALS['wfpl_session_auth']['role'] = $user['role'];
+ $GLOBALS['wfpl_session_auth']['name'] = $user['name'];
+ $GLOBALS['wfpl_session_auth']['username'] = $user['username'];
+ $GLOBALS['wfpl_session_auth']['last_active'] = $now;
+ }
+
+ if ($password_reset) {
+ $GLOBALS['wfpl_session_auth']['password_reset'] = true;
+ $GLOBALS['wfpl_session_auth']['id'] = session_get('auth_password_reset_id');
+ }
+}
+
+# return an assoc containing info about the authenticated user, see session_auth_init
+function session_auth() {
+ if (!isset($GLOBALS['wfpl_session_auth'])) {
+ $id = false;
+ $reset = false;
+ if (session_exists()) {
+ $id = session_get('auth_id');
+ if (!$id) {
+ $r = session_get('auth_password_reset');
+ if (strlen($r)) {
+ $r = (int) format_int_0($r);
+ if (time() < $r) {
+ $reset = true;
+ } else {
+ message('Oops, your temporary access (to change your password) has expired');
+ session_clear('auth_password_reset');
+ }
+ }
+ }
+ }
+ session_auth_init($id, $reset);
+ }
+ return $GLOBALS['wfpl_session_auth'];
+}
+
+# return true if the logged in user is allowed to $priv
+# (false if they are not logged in, or aren't alowed to $priv)
+function session_auth_can($priv) {
+ $s = session_auth();
+ if ($s['role'] === 'admin') {
+ return true;
+ }
+ return false;
+}
+
+# return ONLY IF the currently logged in user can $priv
+# otherwise, it displays the login page, and exit early
+function session_auth_must($priv) {
+ if (session_auth_can($priv)) {
+ return;
+ }
+ if (!isset($_REQUEST['after_login'])) {
+ $_REQUEST['after_login_url'] = this_url();
+ }
+ wfpl_main('login');
+ exit();
+}
--- /dev/null
+<!DOCTYPE html>
+
+<html>
+<head>
+ <title></title>
+</head>
+
+<body>
+ <!--~$body show {~-->
+ <!--~form {~-->
+ <form action="login" method="post">
+ <div class="caption">Username</div>
+ <div class="field"><input type="text" name="username" value="~username attr~" autofocus></div>
+
+ <div class="caption">Password (case sensitive)</div>
+ <div class="field"><input type="password" name="password" value=""></div>
+
+ <div class="caption"> </div>
+ <div class="field"><input type="hidden" name="after_login_url" value="~after_login_url attr~"><input type="submit" value="Log in"></div>
+ </form>
+ <!--~}~-->
+ <!--~}~-->
+</body>
+</html>
--- /dev/null
+<?php
+
+
+function login_get_fields() {
+ $data = array();
+
+ $data['after_login_url'] = format_oneline(_REQUEST_cut('after_login_url'));
+ $data['username'] = format_oneline(trim(_REQUEST_cut('username')));
+ $data['password'] = format_oneline(trim(_REQUEST_cut('password')));
+
+ return $data;
+}
+
+function login_main() {
+ $data = login_get_fields();
+ if (strlen($data['username']) && strlen($data['password'])) {
+ $row = db_get_assoc('users', 'id,name,role,password', 'where username=%"', format_auth_username($data['username']));
+ if ($row) # &&
+ if (strlen($row['password'])) {
+ $needs_rehash = false;
+ $password_good = false;
+ if (substr($row['password'], 0, 5) === 'sha1:') {
+ if (sha1($data['password']) === substr($row['password'], 5)) {
+ $password_good = true;
+ $needs_rehash = true;
+ }
+ } else {
+ if (!function_exists('password_hash')) {
+ require_once(DOCROOT . 'inc/password_funcs_backported.php');
+ }
+ if (password_verify($data['password'], $row['password'])) {
+ $password_good = true;
+ if (password_needs_rehash($row['password'], PASSWORD_DEFAULT)) {
+ $needs_rehash = true;
+ }
+ }
+ }
+ if ($password_good) {
+ if ($needs_rehash) {
+ $hash = password_hash($data['password'], PASSWORD_DEFAULT);
+ db_update('users', 'password', $hash, 'where id=%i', $row['id']);
+ }
+
+ session_new();
+ session_set('auth_id', $row['id']);
+ # we're about to http redirect, so no need to update session_auth now
+ db_update('users', 'last_login', time(), 'where id=%i', $row['id']);
+ message("You are now logged in.");
+ if(!$data['after_login_url']) {
+ if ($row['role'] == 'admin') {
+ $data['after_login_url'] = './admin';
+ } else {
+ $data['after_login_url'] = './';
+ }
+ } elseif(strpos(':', $data['after_login_url']) !== false) {
+ $data['after_login_url'] = "./$data[url]";
+ }
+
+ # redirect to the page they were trying to access:
+ return $data['after_login_url'];
+ }
+ }
+ message("Incorrect username and/or password");
+ }
+ $data['password'] = '';
+ tem_set('form', $data);
+}