Browse Source

Merge branch '6_restrict_opens' of kim/npaste into dev

tags/v0.6.5
Kim Grytøyr 1 year ago
committed by Gitea
parent
commit
09c96396dc
3 changed files with 86 additions and 69 deletions
  1. +16
    -3
      cli-script/npaste
  2. +41
    -0
      src/lib/helpers.js
  3. +29
    -66
      src/lib/paste.js

+ 16
- 3
cli-script/npaste View File

@@ -10,6 +10,7 @@ usage() {
printf "\t-p --plaintext No syntax highlighting.\n"
printf "\t--get PASTE_ID Downloads a paste and (optionally decrypts it and) saves it to the current folder.\n"
printf "\t--age When to delete this item. Syntax: n[m|h|d|y], where n is an integer. minutes, hours, days, years. Example: 1h. Default: 0, ie. never deleted.\n"
printf "\t--max-opens Restrict how many times this paste can be opened.\n"
printf "\t--encrypt The paste will be encrypted using a secret key not known to the server.\n"
printf "\t--vault If using --encrypt, this will also encrypt it with a password that is not in the URL.\n"
printf "\t--archive The paste will be restorable for the submitter with the archive flag.\n"
@@ -53,10 +54,12 @@ NPASTE_VAULTS="$HOME/.config/npaste/vaults"
NPASTE_FILE="-" # default stdin
NPASTE_USE_AUTO_PIPE_COMMAND=1
NPASTE_ARCHIVE=0
NPASTE_MAX_OPENS=0
NPASTE_NO_ARCHIVE=0

# override from config if no age has been set
# override from config if no value has been set
NPASTE_AGE_HAS_BEEN_SET=0
NPASTE_MAX_OPENS_HAS_BEEN_SET=0

# get parameters
while [ "$1" != "" ]; do
@@ -80,6 +83,10 @@ while [ "$1" != "" ]; do
--output)
NPASTE_GET_OUTPUT=1
;;
--max-opens)
NPASTE_MAX_OPENS=$VALUE
NPASTE_MAX_OPENS_HAS_BEEN_SET=1
;;
--age)
NPASTE_AGE=$VALUE
NPASTE_AGE_HAS_BEEN_SET=1
@@ -150,6 +157,12 @@ while read -r line || [[ -n $line ]]; do
default_encrypt)
NPASTE_DEFAULT_ENCRYPT=$VALUE
;;
default_max_opens)
if [ $NPASTE_MAX_OPENS_HAS_BEEN_SET -eq 0 ]; then
# override max opens, as no max opens has been provided at runtime
NPASTE_MAX_OPENS=$VALUE
fi
;;
encryption_key_length)
NPASTE_ENCRYPTION_KEY_LENGTH=$VALUE
;;
@@ -278,9 +291,9 @@ if [ "$NPASTE_DO_ENCRYPT" = "1" ]; then
KEY=$(openssl rand -hex $NPASTE_ENCRYPTION_KEY_LENGTH)

# upload file to npaste
NPASTE_PASTE_URL=$(cat $NPASTE_FILE | base64 | /usr/local/bin/gpg --armor --batch --passphrase "$KEY$NPASTE_VAULT_KEY" --symmetric | api_request $NPASTE_USERNAME $NPASTE_APIKEY $NPASTE_URL "-F paste=@-" "-F plain=$NPASTE_IS_PLAINTEXT" "-F age=$NPASTE_AGE" "-F archive=$NPASTE_DO_ARCHIVE" "-F mimetype=$MIME_TYPE" "-F encrypted=1" "-F vault=$NPASTE_VAULT")
NPASTE_PASTE_URL=$(cat $NPASTE_FILE | base64 | /usr/local/bin/gpg --armor --batch --passphrase "$KEY$NPASTE_VAULT_KEY" --symmetric | api_request $NPASTE_USERNAME $NPASTE_APIKEY $NPASTE_URL "-F paste=@-" "-F plain=$NPASTE_IS_PLAINTEXT" "-F maxopens=$NPASTE_MAX_OPENS" "-F age=$NPASTE_AGE" "-F archive=$NPASTE_DO_ARCHIVE" "-F mimetype=$MIME_TYPE" "-F encrypted=1" "-F vault=$NPASTE_VAULT")
else
NPASTE_PASTE_URL=$(cat $NPASTE_FILE | api_request $NPASTE_USERNAME $NPASTE_APIKEY $NPASTE_URL "-F paste=@-" "-F plain=$NPASTE_IS_PLAINTEXT" "-F age=$NPASTE_AGE" "-F archive=$NPASTE_DO_ARCHIVE" "-F encrypted=0")
NPASTE_PASTE_URL=$(cat $NPASTE_FILE | api_request $NPASTE_USERNAME $NPASTE_APIKEY $NPASTE_URL "-F paste=@-" "-F plain=$NPASTE_IS_PLAINTEXT" "-F maxopens=$NPASTE_MAX_OPENS" "-F age=$NPASTE_AGE" "-F archive=$NPASTE_DO_ARCHIVE" "-F encrypted=0")
fi

case "$NPASTE_PASTE_URL" in


+ 41
- 0
src/lib/helpers.js View File

@@ -4,6 +4,31 @@ const fs = require('fs');
// local modules
const config = require('./config').getConfig();

// Check if paste is unavailable
exports.pasteIsUnavailable = (paste, res) => {
if (paste === false) {
return res.status(400).send('Paste not found');
}

if (paste.archived) {
return res.status(400).send('This paste has been archived and is no longer publicly available.');
}

if (paste.expired || paste.data == null) {
return res.status(400).send('This paste has expired and is no longer available.');
}

if (paste.metadata.maxOpens && paste.metadata.maxOpens > 0 && paste.metadata.timesOpened && paste.metadata.timesOpened >= paste.metadata.maxOpens) {
return res.status(400).send('This paste has reached its maximum number of views');
}

// Do not show pastes have a currently invalid mime type
if (this.validateMimeType(paste) === false || typeof paste.metadata.contentType === 'undefined') {
return res.status(500).send('Invalid type');
}

return false;
}

// Get metadata for a paste
exports.getMetadata = (id, path) => {
@@ -11,12 +36,28 @@ exports.getMetadata = (id, path) => {
return JSON.parse(fs.readFileSync(path + id + '.meta'));
}

// Increase number of times paste has been opened
exports.increaseTimesOpened = (paste) => {
if (paste.metadata.timesOpened) {
paste.metadata.timesOpened++;
} else {
paste.metadata.timesOpened = 1;
}

this.saveMetadata(paste.metadata.id, paste.metadata);
}

exports.validateMimeType = (paste) => {
if (config.mime_types_blacklist.indexOf(paste.metadata.contentType) !== -1) return false;

return true;
}

// Save metadata for a paste
exports.saveMetadata = (id, metadata) => {
fs.writeFileSync(config.path + id + '.meta', JSON.stringify(metadata));
}


// Parse the "age" POST field and return a correct age.
exports.parseAge = (age) => {


+ 29
- 66
src/lib/paste.js View File

@@ -4,7 +4,7 @@ const moment = require('moment');
const basicAuth = require('basic-auth');
const crypto = require('crypto');
const mmm = require('mmmagic'),
Magic = mmm.Magic;
Magic = mmm.Magic;

// local modules
const helpers = require('../lib/helpers');
@@ -41,22 +41,13 @@ const getPaste = (pasteId) => {

const download = (req, res, next) => {
const paste = getPaste(req.params.paste);
if (paste === false) {
return res.status(400).send('Paste not found');
}

if (paste.archived) {
return res.status(400).send('This paste has been archived and is no longer publicly available.');
}

if (paste.expired || paste.data == null) {
return res.status(400).send('This paste has expired and is no longer available.');
const pasteIsUnavailable = helpers.pasteIsUnavailable(paste, res);
if (pasteIsUnavailable !== false) {
return pasteIsUnavailable;
}

// Do not show pastes have a currently invalid mime type
if (helpers.validateMimeType(paste) === false || typeof paste.metadata.contentType === 'undefined') {
return res.status(500).send('Invalid type');
}
helpers.increaseTimesOpened(paste);

console.log(paste);

@@ -66,21 +57,10 @@ const download = (req, res, next) => {

const filename = (req, res, next) => {
const paste = getPaste(req.params.paste);
if (paste === false) {
return res.status(400).send('Paste not found');
}

if (paste.archived) {
return res.status(400).send('This paste has been archived and is no longer publicly available.');
}

if (paste.expired || paste.data == null) {
return res.status(400).send('This paste has expired and is no longer available.');
}

// Do not show pastes have a currently invalid mime type
if (helpers.validateMimeType(paste) === false || typeof paste.metadata.contentType === 'undefined') {
return res.status(500).send('Invalid type');
const pasteIsUnavailable = helpers.pasteIsUnavailable(paste, res);
if (pasteIsUnavailable !== false) {
return pasteIsUnavailable;
}

res.status(200).send(paste.metadata.id + '.' + paste.metadata.extension + (paste.metadata.encrypted ? '.gpg' : ''));
@@ -88,16 +68,10 @@ const filename = (req, res, next) => {

const getFormatted = (req, res, next) => {
const paste = getPaste(req.params.paste);
if (paste === false) {
return res.status(400).send('Paste not found');
}

if (paste.archived) {
return res.status(400).send('This paste has been archived and is no longer publicly available.');
}

if (paste.expired || paste.data == null) {
return res.status(400).send('This paste has expired and is no longer available.');
const pasteIsUnavailable = helpers.pasteIsUnavailable(paste, res);
if (pasteIsUnavailable !== false) {
return pasteIsUnavailable;
}

// TODO: Cleanup/simplification
@@ -117,11 +91,6 @@ const getFormatted = (req, res, next) => {
version: config.version
};

// Do not show pastes have a currently invalid mime type
if (helpers.validateMimeType(paste) === false) {
return res.status(500).send('Invalid type');
}

if (paste.metadata.type == 'text' || paste.metadata.contentType.split('/')[0] == 'text') {
// We're good..
} else if (paste.metadata.type == "image" || paste.metadata.contentType.split('/')[0] == 'image') {
@@ -132,27 +101,21 @@ const getFormatted = (req, res, next) => {
} else {
return res.status(500).send('Invalid type');
}

helpers.increaseTimesOpened(paste);

res.render(template, options);
}

const getRaw = (req, res, next) => {
const paste = getPaste(req.params.paste);
if (paste === false) {
return res.status(400).send('Paste not found');
}

if (paste.archived) {
return res.status(400).send('This paste has been archived and is no longer publicly available.');
const pasteIsUnavailable = helpers.pasteIsUnavailable(paste, res);
if (pasteIsUnavailable !== false) {
return pasteIsUnavailable;
}

if (paste.expired || paste.data == null) {
return res.status(400).send('This paste has expired and is no longer available.');
}
// Do not show pastes have a currently invalid mime type
if (helpers.validateMimeType(paste) === false || typeof paste.metadata.contentType === 'undefined') {
return res.status(500).send('Invalid type');
}
helpers.increaseTimesOpened(paste);

if (paste.metadata.type === "text") {
res.setHeader("Content-Type", "text/plain");
@@ -163,16 +126,11 @@ const getRaw = (req, res, next) => {

const getMeta = (req, res, next) => {
const paste = getPaste(req.params.paste);
if (paste === false) {
return res.status(400).send('Paste not found');
}

if (paste.archived) {
return res.status(400).send('This paste has been archived and is no longer publicly available.');
}
const pasteIsUnavailable = helpers.pasteIsUnavailable(paste, res);

if (paste.expired || paste.data == null) {
return res.status(400).send('This paste has expired and is no longer available.');
if (pasteIsUnavailable !== false) {
return pasteIsUnavailable;
}

res.setHeader("Content-Type", "application/json");
@@ -213,7 +171,7 @@ const add = (req, res, next) => {
fs.unlinkSync(req.file.path);
return res.status(400).send('MIME type not allowed: ' + mimeType);
}
if (config.mime_types[mimeType]) {
type = config.mime_types[mimeType].type;
contentType = config.mime_types[mimeType].mime_type;
@@ -232,6 +190,7 @@ const add = (req, res, next) => {
contentType: contentType,
extension: extension,
submitter: user.name,
timesOpened: 0,
encrypted: req.body.encrypted == 1 ? true : false,
}

@@ -239,6 +198,11 @@ const add = (req, res, next) => {
// TODO: Consider if this should be an option in the UI instead?
metadata.plain = req.body.plain == 1 ? true : false;

// Can this paste only be opened X times?
if (req.body.maxopens && parseInt(req.body.maxopens) > 0) {
metadata.maxOpens = parseInt(req.body.maxopens);
}

// If provided, set paste age. If not provided, use default age from config.
// Age of 0 means no expiration.
metadata.expiresAt = req.body.age ? helpers.parseAge(req.body.age) : null;
@@ -253,8 +217,7 @@ const add = (req, res, next) => {
}

// Create .meta file
// TODO: Move this to function or module
fs.writeFileSync(config.path + filename + '.meta', JSON.stringify(metadata));
helpers.saveMetadata(filename, metadata);

// Move uploaded file to its final destination
fs.renameSync(req.file.path, config.path + filename + '.' + extension);


Loading…
Cancel
Save