Armored-Dragon 2024-05-01 11:44:41 -05:00 committed by GitHub
commit 596f476a4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 112 additions and 78 deletions

View File

@ -5,6 +5,8 @@ const { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand, ListO
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
let s3;
const crypto = require("crypto");
const validate = require("../form_validation");
const permissions = require("../permissions");
const md = require("markdown-it")()
.use(require("markdown-it-underline"))
.use(require("markdown-it-footnote"))
@ -59,22 +61,23 @@ function _initS3Storage() {
// Users
async function newUser({ username, password, role } = {}) {
if (!username) return _r(false, "Username not specified");
if (!password) return _r(false, "Password not specified");
// Sanity check on user registration.
const valid = validate.newUser({ username: username, password: password });
if (!valid.success) return _r(false, valid.message);
// Create the account
try {
user_database_entry = await prisma.user.create({ data: { username: username, password: password, role: role } });
} catch (e) {
let message = "Unknown error";
return { success: false, message: message };
return _r(false, message);
}
// Create the profile page and link
try {
user_profile_database_entry = await prisma.profilePage.create({ data: { owner: { connect: { id: user_database_entry.id } } } });
} catch (e) {
return { success: false, message: `Error creating profile page for user ${username}` };
return _r(false, `Error creating profile page for user ${username}`);
}
// Master user was created; server initialized
@ -95,6 +98,7 @@ async function getUser({ user_id, username, include_password = false }) {
return { success: true, data: user };
}
// TODO: Rename patchUser
async function editUser({ requester_id, user_id, user_content }) {
let user = await getUser({ user_id: user_id });
if (!user.success) return _r(false, "User not found");
@ -226,29 +230,24 @@ async function getPost({ requester_id, post_id, visibility = "PUBLISHED" } = {},
return pageList.slice(0, 5);
}
}
// TODO: Rename patchPost
async function editPost({ requester_id, post_id, post_content }) {
let user = await getUser({ user_id: requester_id });
let post = await getPost({ post_id: post_id });
let publish_date = null;
if (!user.success) return _r(false, post.message || "User not found");
user = user.data;
if (!post.success) return _r(false, post.message || "Post not found");
post = post.data;
// Validate the post content
let validated_post = validate.patchPost(post_content, user, post);
if (!validated_post.success) return _r(false, validated_post.message);
// Check to see if the requester can update the post
// TODO: Permissions
let can_update = post.owner.id === user.id || user.role === "ADMIN";
user = validated_post.data.user;
post = validated_post.data.post;
validated_post = validated_post.data.post_formatted;
// FIXME: Unsure if this actually works
// Check if we already have a formatted publish date
if (typeof post.publish_date !== "object") {
const [year, month, day] = post.date.split("-");
const [hour, minute] = post.time.split(":");
publish_date = new Date(year, month - 1, day, hour, minute);
}
// Check if the user can preform the action
const can_act = permissions.patchPost(post, user);
if (!can_act.success) return _r(false, can_act.message);
// Handle tags ----
// Handle tags ----------
let database_tag_list = [];
const existing_tags = post.tags?.map((tag) => ({ name: tag })) || [];
@ -264,12 +263,10 @@ async function editPost({ requester_id, post_id, post_content }) {
// Rebuild the post to save
let post_formatted = {
title: post_content.title,
description: post_content.description,
content: post_content.content,
visibility: post_content.visibility || "PRIVATE",
publish_date: publish_date || post_content.publish_date,
...validated_post,
// Handle tag changes
tags: { disconnect: [...existing_tags], connect: [...database_tag_list] },
// Handle media changes
media: [...post.raw_media, ...post_content.media],
};
@ -327,18 +324,22 @@ async function getBiography({ requester_id, author_id }) {
return { success: true, data: post };
}
// TODO: Rename to patchBiography
async function updateBiography({ requester_id, author_id, biography_content }) {
let user = await getUser({ user_id: requester_id });
let biography = await getBiography({ author_id: author_id });
if (!user.success) return _r(false, user.message || "Author not found");
user = user.data;
// Validate post ----------
let formatted_biography = validate.patchBiography(biography_content, user, biography);
if (!formatted_biography.success) return _r(false, formatted_biography.message);
if (!biography.success) return _r(false, biography.message || "Post not found");
biography = biography.data;
user = formatted_biography.data.user;
biography = formatted_biography.data.biography;
biography_content = formatted_biography.data.biography_content;
let can_update = biography.owner.id === user.id || user.role === "ADMIN";
if (!can_update) return _r(false, "User not permitted");
// Permission check ----------
const can_act = permissions.patchBiography(biography_content, user, biography);
if (!can_act.success) return _r(false, "User not permitted");
let formatted = {
content: biography_content.content,

View File

@ -57,18 +57,7 @@ async function deleteBlog(req, res) {
return res.json(await core.deletePost({ post_id: req.body.id, requester_id: req.session.user.id }));
}
async function patchBlog(req, res) {
// FIXME: validate does not return post id
// Can user change post?
// User is admin, or user is author
// Validate blog info
let valid = await validate.postBlog(req.body);
if (!valid.success) return { success: false, message: valid.message || "Post failed validation" };
valid = valid.data;
// TODO: Permissions for updating blog
return res.json(await core.editPost({ requester_id: req.session.user.id, post_id: req.body.id, post_content: valid }));
return res.json(await core.editPost({ requester_id: req.session.user.id, post_id: req.body.id, post_content: req.body }));
}
async function patchBiography(request, response) {
// TODO: Validate

View File

@ -1,33 +1,39 @@
//
// Form validation
//
// Preform sanity checks on content
// Format given data in an accessible way
//
const core = require("./core/core");
async function registerUser(username, password) {
if (!username) return { success: false, message: "No username provided" };
if (!password) return { success: false, message: "No password provided" };
if (password.length < core.settings["USER_MINIMUM_PASSWORD_LENGTH"]) return { success: false, message: "Password not long enough" };
// Make sure the user registration data is safe and valid.
function newUser({ username, password } = {}) {
if (!username) return _r(false, "No username provided");
if (!password) return _r(false, "No password provided");
if (password.length < core.settings["USER_MINIMUM_PASSWORD_LENGTH"]) return _r(false, `Password is not long enough. Minimum length is ${core.settings["USER_MINIMUM_PASSWORD_LENGTH"]}`);
// Check if username only uses URL safe characters
if (!_isUrlSafe(username)) return { success: false, message: "Username is not URL safe" };
// TODO: Method to block special characters
if (!_isUrlSafe(username)) return _r(false, "Invalid Username. Please only use a-z A-Z 0-9");
// All good! Validation complete
return { success: true };
return _r(true);
}
async function postBlog(blog_object) {
// TODO: Validate blog posts before upload
// Check title length
// Check description length
// Check content length
// Check valid date
// Return formatted object
function patchPost(post_content, user, post) {
let post_formatted = {}; // The formatted post content object that will be returned upon success
let publish_date; // Time and date the post should be made public
let tags = []; // An array of tags for the post
if (!user.success) return _r(false, "User not found");
if (!post.success) return _r(false, "Post not found");
// Get the publish date in a standard format
const [year, month, day] = blog_object.date.split("-");
const [hour, minute] = blog_object.time.split(":");
let publish_date = new Date(year, month - 1, day, hour, minute);
const [year, month, day] = post_content.date.split("-");
const [hour, minute] = post_content.time.split(":");
publish_date = new Date(year, month - 1, day, hour, minute);
// Go though our tags and ensure they are:
let valid_tag_array = [];
blog_object.tags.forEach((tag) => {
// Go though tags list, and turn into a pretty array
post_content.tags.forEach((tag) => {
// Trimmed
tag = tag.trim();
@ -35,27 +41,41 @@ async function postBlog(blog_object) {
tag = tag.toLowerCase();
// Non-empty
if (tag.length !== 0) valid_tag_array.push(tag);
if (tag.length !== 0) tags.push(tag);
});
// Format our data to save
let blog_post_formatted = {
title: blog_object.title,
description: blog_object.description,
content: blog_object.content,
visibility: blog_object.visibility,
delete post_content.date;
delete post_content.time;
// Format the post content
post_formatted = {
// Autofill the given data
...post_content,
// Update tags to our pretty array
tags: tags,
// Update date
publish_date: publish_date,
tags: valid_tag_array,
media: blog_object.media,
thumbnail: blog_object.thumbnail,
};
return { success: true, data: blog_post_formatted };
return _r(true, null, { post_formatted: post_formatted, user: user.data, post: post.data });
}
function patchBiography(biography_content, user, biography) {
if (!user.success) return _r(false, "User not found");
if (!biography.success) return _r(false, "Post not found");
return _r(true, null, { biography_content: biography_content, user: user.data, biography: biography.data });
}
// Helper functions --------------------
function _isUrlSafe(str) {
const pattern = /^[A-Za-z0-9\-_.~]+$/;
return pattern.test(str);
}
function _r(s, m, d) {
return { success: s, message: m ? m || "Unknown error" : undefined, data: d };
}
module.exports = { registerUser, postBlog };
module.exports = { newUser, patchPost, patchBiography };

View File

@ -1,3 +1,26 @@
function postBlog(user) {}
//
// Permissions
//
// Check if a given user has permissions to preform an action
//
module.exports = { postBlog };
// Updating a blog post
function patchPost(post_content, user) {
// Admins can always update any post
if (user.role === "ADMIN") return _r(true);
// User owns the post
if (post_content.owner.id === user.id) return _r(true);
// User is not permitted
return _r(false, "User is not permitted to preform action.");
}
function patchBiography(biography, user) {
// Biographies are just fancy posts right now.
return patchPost(biography, user);
}
function _r(s, m, d) {
return { success: s, message: m ? m || "Unknown error" : undefined, data: d };
}
module.exports = { patchPost, patchBiography };

View File

@ -7,6 +7,7 @@
"login": "/ejs/login.ejs",
"register": "/ejs/register.ejs",
"author": "/ejs/author.ejs",
"authorEdit": "/ejs/authorEdit.ejs",
"post": "/ejs/post.ejs",
"postSearch": "/ejs/postSearch.ejs",
"postNew": "/ejs/postNew.ejs",