Restructure src, import passwd, fix user editing

This commit is contained in:
Felix Albrigtsen 2023-02-22 00:29:33 +01:00
parent 621e3029d7
commit 631cfe158a
6 changed files with 562 additions and 141 deletions

View File

@ -4,14 +4,14 @@
<img src="/assets/logo_black_thicc.png" class="m-0 image is-32x32"> <img src="/assets/logo_black_thicc.png" class="m-0 image is-32x32">
</a> </a>
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample"> <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbar">
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
</a> </a>
</div> </div>
<div id="navbarBasicExample" class="navbar-menu"> <div id="navbar" class="navbar-menu">
<div class="navbar-start"> <div class="navbar-start">
<a href="/" class="navbar-item"> <a href="/" class="navbar-item">
Home Home
@ -27,13 +27,16 @@
</a> </a>
<div class="navbar-dropdown"> <div class="navbar-dropdown">
<a href="/user.php" class="navbar-item"> <a href="/user/index.php" class="navbar-item">
User Details User Details
</a> </a>
<a href="/paymentphp" class="navbar-item"> <a href="/user/import.php" class="navbar-item">
Import Users
</a>
<a href="/payment/index.php" class="navbar-item">
Payments Payments
</a> </a>
<a href="/quota.php" class="navbar-item"> <a href="/quota/index.php" class="navbar-item">
Quota Management Quota Management
</a> </a>
<hr class="navbar-divider"> <hr class="navbar-divider">

175
src/payment/index.php Normal file
View File

@ -0,0 +1,175 @@
<?php
include("../db_connect.php");
?>
<!DOCTYPE html>
<html lang="en" class="has-background-light">
<head>
<?php
$title = "PVVMDB - Payments";
include "../includes/head.php";
?>
</head>
<body>
<?php
include "../includes/nav.php";
?>
<div class="container box">
<h1 class="title">Add Payments</h1>
<div class="columns">
<div class="column card has-background-info-light">
<div class="card-content">
<div class="content">
<h2 class="subtitle">Import GNUCash</h2>
<p>Import a series of payments from a GNUCash file.</p>
</div>
</div>
<footer class="card-footer">
<a href="#" class="card-footer-item">Save</a>
<a href="#" class="card-footer-item">Edit</a>
<a href="#" class="card-footer-item">Delete</a>
</footer>
</div>
<div class="column card has-background-warning-light">
<div class="card-content">
<div class="content">
<h2 class="subtitle">Enter manually</h2>
<p>Fill a submission form to manually register a payment.</p>
</div>
</div>
<footer class="card-footer">
<a href="#" class="card-footer-item">Save</a>
<a href="#" class="card-footer-item">Edit</a>
<a href="#" class="card-footer-item">Delete</a>
</footer>
</div>
</div>
<div class="container box">
<h1 class="title">Payments by user</h1>
<?php if (isset($_GET['username'])): ?>
<?php
// TODO: 404 if user not found
$username = $_GET['username'];
$userquery = pg_prepare($dbconn, "userquery", "SELECT * FROM users WHERE username = $1");
$userquery = pg_execute($dbconn, "userquery", array($username));
$user = pg_fetch_assoc($userquery);
$mtype_result = pg_query($dbconn, "SELECT * FROM membership_types");
$mtypes = pg_fetch_all($mtype_result);
?>
<h2 class="subtitle">Membership payments by <?php echo $_GET['username']; ?></h2>
<table class="table is-fullwidth is-striped">
<colgroup>
<col span="1" style="width: 15%;">
<col span="1" style="width: 15%;">
<col span="1" style="width: 30%;">
<col span="1" style="width: 30%;">
<col span="1" style="width: 10%;">
</colgroup>
<thead>
<tr>
<th>Payment date</th>
<th>Amount Paid</th>
<th>Comment</th>
<th>Membership type</th>
<th>Valid periods</th>
</tr>
</thead>
<tbody>
<?php
$query = pg_prepare($dbconn, "membershippurchases", "SELECT membership_purchases.*, membership_types.name AS membership_type_name, membership_types.price FROM membership_purchases LEFT JOIN membership_types ON membership_purchases.membership_type = membership_types.id WHERE membership_purchases.user_id = $1");
$query = pg_execute($dbconn, "membershippurchases", array($user['id']));
while ($row = pg_fetch_assoc($query)) {
echo "<tr>";
echo "<td>" . $row['created_at'] . "</td>";
echo "<td>" . $row['amount_paid'] . " kr</td>";
echo "<td>" . $row['comment'] . "</td>";
if ($row['membership_type'] == 1) {
echo "<td class='has-background-success' colspan=2>" . $row['membership_type_name'] . "</td>";
} else {
echo "<td>" . $row['membership_type_name'] . "</td>";
echo "<td>" . round($row['amount_paid'] / $row['price'], 2) . "</td>";
}
echo "</tr>";
}
?>
</tbody>
</table>
<h2 class="subtitle">Disk Quota payments by <?php echo $_GET['username']; ?></h2>
<table class="table is-fullwidth is-striped">
<colgroup>
<col span="1" style="width: 15%;">
<col span="1" style="width: 15%;">
<col span="1" style="width: 30%;">
<col span="1" style="width: 30%;">
<col span="1" style="width: 10%;">
</colgroup>
<thead>
<tr>
<th>Payment date</th>
<th>Amount Paid</th>
<th>Comment</th>
<th>Price per GiB</th>
<th>MiB</th>
</tr>
</thead>
<tbody>
<?php
$quotaquery = pg_prepare($dbconn, "quotaquery", "SELECT *,(1024*amount_paid/size_mb) AS price_per_gib FROM disk_purchases WHERE user_id = $1");
$quotaquery = pg_execute($dbconn, "quotaquery", array($user['id']));
$total_quota = 0;
while ($row = pg_fetch_assoc($quotaquery)) {
$total_quota += $row['size_mb'];
echo "<tr>";
echo "<td>" . $row['created_at'] . "</td>";
echo "<td>" . $row['amount_paid'] . "</td>";
echo "<td>" . $row['comment'] . "</td>";
echo "<td>" . round($row['price_per_gib'] , 2) . " kr</td>";
echo "<td>" . round($row['size_mb'] , 2) . "</td>";
echo "</tr>";
}
?>
<tr>
<td colspan=4>Total</td>
<td><?php echo $total_quota ?> MiB</td>
</tr>
</tbody>
</table>
<?php else: ?>
<div class="notification is-info">
<h1 class="title">Select a user</h1>
<form method="get" class="columns">
<div class="column is-10">
<select name="username" class="input select">
<?php
$users = pg_query($dbconn, "SELECT username FROM users");
while ($user = pg_fetch_result($users, 0)) {
echo "<option value='$user'>$user</option>";
}
?>
</select>
</div>
<input type="submit" value="Select" class="column is-2 is-primary button py-2 is-fullwidth is-large">
</form>
</div>
<?php endif; ?>
</div>
</body>
</html>

View File

@ -1,136 +0,0 @@
<?php
include("db_connect.php");
if (isset($_GET['username'])) {
$query = pg_prepare($dbconn, "userid_by_username", 'SELECT id FROM users WHERE username = $1');
$result = pg_execute($dbconn, "userid_by_username", array($_GET['username']));
$row = pg_fetch_row($result);
$userid = $row[0];
if ($userid) {
header("Location: /user.php?id=$userid");
} else {
header("Location: /user.php?id=-1");
}
}
?>
<!DOCTYPE html>
<html lang="en" class="has-background-light">
<head>
<?php
$title = "PVVMDB - User";
include "includes/head.php";
?>
</head>
<body>
<?php
include "includes/nav.php";
?>
<div class="container box">
<?php
if (isset($_GET['id'])) {
$id = $_GET['id'];
$query = pg_prepare($dbconn, "user_by_id", 'SELECT * FROM users WHERE id = $1');
$query = pg_execute($dbconn, "user_by_id", array($id));
$user = pg_fetch_assoc($query);
}
?>
<?php if (isset($_GET['id']) && $user != null): ?>
<div class="notification is-primary">
<h1 class="title">Show / edit <?php echo $user['username']; ?></h1>
</div>
<form>
<label class="label">Username</label>
<input class="input" type="text" name="username" value="<?php echo $user['username']; ?>">
<label class="label">Name</label>
<input class="input" type="text" name="name" value="<?php echo $user['name']; ?>">
<label class="label">Email</label>
<input class="input" type="text" name="external_email" value="<?php echo $user['external_email']; ?>">
<label class="label">Phone</label>
<input class="input" type="text" name="phone" value="<?php echo $user['phone']; ?>">
<label class="label">Locked</label>
<input type="checkbox" name="locked" <?php if ($user['locked']=="t") echo "checked"; ?>>
<label class="label">Created at</label>
<input class="input" type="text" name="created_at" value="<?php echo $user['created_at']; ?>" disabled>
<hr />
<div class="columns">
<input class="button is-primary column m-2" type="submit" value="Save Changes" />
<!-- <a class="button is-danger column m-2" href="/user.php?id=<?php echo $user['id']; ?>&delete=true">Delete</a> -->
</div>
</form>
<?php else: ?>
<?php if(isset($_GET['id'])): ?>
<div class="notification is-warning">
<h2 class="title">User not found</h2>
<p>The user you are looking for does not exist.</p>
<p>Please select a user from the list or enter a username.</p>
</div>
<?php else: ?>
<div class="notification is-info">
<h2 class="title">No user selected.</h2>
<p>Please select a user from the list or enter a username.</p>
</div>
<?php endif; ?>
<form method="GET" class="field has-addons my-4">
<div class="control is-expanded">
<input class="input" name="username" type="text" placeholder="Find a user">
</div>
<div class="control">
<a class="button is-link is-light">
Edit user
</a>
</div>
</form>
<table class="table is-fullwidth">
<thead>
<tr>
<th>Username</th>
<th>Name</th>
<th>Email</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
<?php
//TODO: Pagination
$query = "SELECT * FROM users";
$result = pg_query($dbconn, $query);
while ($row = pg_fetch_assoc($result)) {
echo "<tr>";
echo "<td><a href='user.php?id=" . $row['id'] . "'>" . $row['username'] . "</a></td>";
echo "<td>" . $row['name'] . "</td>";
echo "<td>" . $row['external_email'] . "</td>";
echo "<td><a class='button is-primary' href='user.php?id=" . $row['id'] . "'>Edit</a></td>";
echo "</tr>";
}
?>
</tbody>
<?php endif; ?>
</body>
</html>

141
src/user/edit.php Normal file
View File

@ -0,0 +1,141 @@
<?php
include("../db_connect.php");
if (isset($_GET['username'])) {
$query = pg_prepare($dbconn, "userid_by_username", 'SELECT id FROM users WHERE username = $1');
$result = pg_execute($dbconn, "userid_by_username", array($_GET['username']));
$row = pg_fetch_row($result);
$userid = $row[0];
if ($userid) {
header("Location: /user/edit.php?id=$userid");
} else {
header("Location: /user/edit.php?id=-1");
}
}
$id = $_GET['id'];
$query = pg_prepare($dbconn, "user_by_id", 'SELECT * FROM users WHERE id = $1');
$result = pg_execute($dbconn, "user_by_id", array($id));
$user = pg_fetch_assoc($result);
$update_status = "none";
$update_message = "";
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$orig_username = $user['username'];
if (isset($_POST['username'])) $user['username'] = $_POST['username'];
if (isset($_POST['name'])) $user['name'] = $_POST['name'];
if (isset($_POST['external_email'])) $user['external_email'] = $_POST['external_email'];
if (isset($_POST['phone'])) $user['phone'] = $_POST['phone'];
if (isset($_POST['comment'])) $user['comment'] = $_POST['comment'];
if (isset($_POST['locked'])) {
$user['locked'] = $_POST['locked'] == "yes" ? "t" : "f";
}
do {
// TODO: Validate input
// Check that username is unique
if ($user['username'] != $orig_username) {
$query = pg_prepare($dbconn, "user_by_username", 'SELECT id FROM users WHERE username = $1');
$result = pg_execute($dbconn, "user_by_username", array($user['username']));
if (pg_num_rows($result) > 0) {
$update_status = "error";
$update_message = "Username already exists";
break;
}
}
$update_query = pg_prepare($dbconn, "update_user", 'UPDATE users SET username = $1, name = $2, external_email = $3, phone = $4, comment = $5, locked = $6 WHERE id = $7');
$update_result = pg_execute($dbconn, "update_user", array($user['username'], $user['name'], $user['external_email'], $user['phone'], $user['comment'], $user['locked'], $id));
if (pg_affected_rows($result) > 0) {
$update_status = "success";
} else {
$update_status = "error";
}
} while (false);
}
$result = pg_execute($dbconn, "user_by_id", array($id));
$user = pg_fetch_assoc($result);
?>
<!DOCTYPE html>
<html lang="en" class="has-background-light">
<head>
<?php
$title = "PVVMDB - User";
include "../includes/head.php";
?>
</head>
<body>
<?php
include "../includes/nav.php";
?>
<div class="container box">
<?php if (isset($_GET['id']) && $user != null): ?>
<div class="notification is-primary">
<h1 class="title">Show / edit <?php echo $user['username']; ?></h1>
</div>
<form action="" method="POST">
<label class="label">Username</label>
<input class="input" type="text" name="username" value="<?php echo $user['username']; ?>">
<label class="label">Name</label>
<input class="input" type="text" name="name" value="<?php echo $user['name']; ?>">
<label class="label">Email</label>
<input class="input" type="text" name="external_email" value="<?php echo $user['external_email']; ?>">
<label class="label">Phone</label>
<input class="input" type="text" name="phone" value="<?php echo $user['phone']; ?>">
<label class="label">Comment</label>
<textarea class="textarea" name="comment"><?php echo $user['comment']; ?></textarea>
<label class="label">Locked</label>
<input type="hidden" name="locked" value="no">
<input type="checkbox" name="locked" <?php if ($user['locked']=="t") echo "checked"; ?> value="yes">
<label class="label">Created at</label>
<input class="input" type="text" name="created_at" value="<?php echo $user['created_at']; ?>" disabled>
<hr />
<div class="columns">
<input class="button is-primary column m-2" type="submit" value="Save Changes" />
<!-- <a class="button is-danger column m-2" href="/user.php?id=<?php echo $user['id']; ?>&delete=true">Delete</a> -->
</div>
</form>
<?php else: ?>
<?php if(isset($_GET['id'])): ?>
<div class="notification is-warning">
<h2 class="title">User not found</h2>
<p>The user you are looking for does not exist.</p>
<p>Please select a user from the list or enter a username.</p>
<a href="/user/index.php" class="button is-large is-error is-outlined">Back to user list</a>
</div>
<?php else: ?>
<div class="notification is-info">
<h2 class="title">No user selected.</h2>
<p>Please select a user from the list or enter a username.</p>
<a href="/user/index.php" class="button is-large is-primary">Back to user list</a>
</div>
<?php endif; ?>
<?php endif; ?>
</body>
</html>

155
src/user/import.php Normal file
View File

@ -0,0 +1,155 @@
<?php
include("../db_connect.php");
$upload_state = "none";
$users_added = 0;
$users_updated = 0;
$users_skipped = 0;
# Handle POST requests
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($_FILES['passwdfile']['error'] == UPLOAD_ERR_OK //checks for errors
&& is_uploaded_file($_FILES['passwdfile']['tmp_name'])) { //checks that file is uploaded
$filedata = file_get_contents($_FILES['passwdfile']['tmp_name']);
$query_select = pg_prepare($dbconn, "select_user_by_username", 'SELECT * FROM users WHERE username = $1');
$query_insert = pg_prepare($dbconn, "insert_user", 'INSERT INTO users (username, name, comment) VALUES ($1, $2, $3)');
$query_update = pg_prepare($dbconn, "update_user", 'UPDATE users SET name = $1, comment = $2 WHERE username = $3');
# For every line in the file
foreach (explode("\n", $filedata) as $line) {
# Split the line into username and password
$line = explode(":", $line);
# Check that the line is valid length (7 colon separated fields)
if (count($line) != 7) {
$users_skipped++;
continue;
}
$username = $line[0];
$userid = $line[2];
$name = $line[4];
$shell = $line[6];
# If the name contains a comma, split it into name and comment
$comma_pos = strpos($name, ",");
if ($comma_pos !== false) {
$comment = substr($name, $comma_pos + 1);
$name = substr($name, 0, $comma_pos);
} else {
$comment = "";
}
# System users with uids under 1000 are skipped
if ($userid < 1000) {
$users_skipped++;
continue;
}
# Users with nologin or /bin/false shells are skipped
if ($shell == "/usr/sbin/nologin" || $shell == "/bin/false") {
$users_skipped++;
continue;
}
# Check if the user already exists
# If they do, update their name
# If they don't, create them
$result = pg_execute($dbconn, "select_user_by_username", array($username));
if (pg_num_rows($result) > 0) {
$result = pg_execute($dbconn, "update_user", array($name, $comment, $username));
if (pg_affected_rows($result) > 0) {
$users_updated++;
} else {
$users_skipped++;
}
} else {
$result = pg_execute($dbconn, "insert_user", array($username, $name, $comment));
if (pg_affected_rows($result) > 0) {
$users_added++;
} else {
$users_skipped++;
}
}
}
$upload_state = "success";
} else {
$upload_state = "error";
}
}
?>
<!DOCTYPE html>
<html lang="en" class="has-background-light">
<head>
<?php
$title = "PVVMDB - Import User";
include "../includes/head.php";
?>
</head>
<body>
<?php
include "../includes/nav.php";
?>
<div class="container box">
<?php if ($upload_state == "success"): ?>
<div class="notification is-success">
<h1 class="title">Import successful</h1>
<p><?php echo "$users_added users added, $users_updated users updated, $users_skipped users skipped" ?></p>
</div>
<?php elseif ($upload_state == "error"): ?>
<div class="notification is-danger">
<h1 class="title">Import failed</h1>
<p>There was an error uploading the file</p>
</div>
<?php endif; ?>
<div class="notification is-info">
<h1 class="title">Import users</h1>
<h3 class="subtitle">Import users by uploading a passwd file</h3>
</div>
<div class="content">
<p class="is-size-5">
Load new users by importing the file found at /etc/passwd.
<br>
Duplicate usernames will be detected and their names will be updated.
<br>
System users with uids under 1000 and users with nologin or /bin/false shells will automatically be skipped.
</p>
</div>
<form action="<?php $_PHP_SELF ?>" method="POST" enctype="multipart/form-data">
<div class="file mb-4">
<label class="file-label">
<input class="file-input" type="file" name="passwdfile">
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
<span class="file-label">
Choose a file…
</span>
</span>
</label>
</div>
<div class="columns mx-2">
<button class="button column is-fullwidth mx-2 is-primary">Submit</button>
<a class="button column is-fullwidth mx-2 is-warning" href="/user/index.php">Return to user list</a>
</div>
</form>
</div>
</body>
</html>

83
src/user/index.php Normal file
View File

@ -0,0 +1,83 @@
<?php
include("../db_connect.php");
if (isset($_GET['username'])) {
$query = pg_prepare($dbconn, "userid_by_username", 'SELECT id FROM users WHERE username = $1');
$result = pg_execute($dbconn, "userid_by_username", array($_GET['username']));
$row = pg_fetch_row($result);
$userid = $row[0];
if ($userid) {
header("Location: /user/index.php?id=$userid");
} else {
header("Location: /user/index.php?id=-1");
}
}
?>
<!DOCTYPE html>
<html lang="en" class="has-background-light">
<head>
<?php
$title = "PVVMDB - User";
include "../includes/head.php";
?>
</head>
<body>
<?php
include "../includes/nav.php";
?>
<div class="container box">
<div class="notification is-primary">
<h1 class="title">Search and edit users</h1>
</div>
<div class="notification is-info">
<h2 class="subtitle">Edit user by exact username</h2>
<form method="GET" class="field has-addons my-4">
<div class="control is-expanded">
<input class="input" name="username" type="text" placeholder="Find a user">
</div>
<div class="control">
<a class="button is-link is-light">
Edit user
</a>
</div>
</form>
</div>
<table class="table is-fullwidth">
<thead>
<tr>
<th>Username</th>
<th>Name</th>
<th>Email</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
<?php
//TODO: Pagination
//TODO: Search
$query = "SELECT * FROM users ORDER BY username ASC";
$result = pg_query($dbconn, $query);
echo "<p>Fetched " . pg_num_rows($result) . " users</p>";
while ($row = pg_fetch_assoc($result)) {
echo "<tr>";
echo "<td><a href='/user/edit.php?id=" . $row['id'] . "'>" . $row['username'] . "</a></td>";
echo "<td>" . $row['name'] . "</td>";
echo "<td>" . ($row['external_email'] ? $row['external_email'] : ($row['username'] . "@pvv.ntnu.no")) . "</td>";
echo "<td><a class='button is-primary' href='/user/edit.php?id=" . $row['id'] . "'>Edit</a></td>";
echo "</tr>";
}
?>
</tbody>
</body>
</html>