fixed the anonymous discrepency

This commit is contained in:
bill bittner 2017-11-03 13:37:23 -07:00
parent d92f499269
commit e34e78c169
8 changed files with 97 additions and 96 deletions

View file

@ -36,17 +36,17 @@ spee.ch is a single-serving site that reads and publishes images and videos to a
#### POST #### POST
* /api/publish * /api/publish
* example: `curl -X POST -F 'name=MyPictureName' -F 'nsfw=false' -F 'file=@/path/to/my/picture.jpeg' https://spee.ch/api/publish` * example: `curl -X POST -F 'name=MyPictureName' -F 'file=@/path/to/myPicture.jpeg' https://spee.ch/api/publish`
* Parameters: * Parameters:
* name (string) * `name`
* nsfw (boolean) * `file` (.mp4, .jpeg, .jpg, .gif, or .png)
* file (.mp4, .jpeg, .jpg, .gif, or .png) * `nsfw` (optional)
* license (string, optional) * `license` (optional)
* title (string, optional) * `title` (optional)
* description (string, optional) * `description` (optional)
* thumbnail (string, optional) (for .mp4 uploads only) * `thumbnail` url to thumbnail image, for .mp4 uploads only (optional)
* channelName(string, optional) * `channelName`(optional)
* channelPassword (string, optional) * `channelPassword` (optional,; required if `channelName` is provided)
## bugs ## bugs
If you find a bug or experience a problem, please report your issue here on github and find us in the lbry slack! If you find a bug or experience a problem, please report your issue here on github and find us in the lbry slack!

View file

@ -2,12 +2,14 @@ const db = require('../models');
const logger = require('winston'); const logger = require('winston');
module.exports = { module.exports = {
authenticateChannelCredentials (channelName, userPassword) { authenticateChannelCredentials (skipAuth, channelName, userPassword) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!channelName) { // skip authentication if not needed
resolve(true); if (skipAuth) {
resolve(skipAuth);
return; return;
} }
// authentication
const userName = channelName.substring(1); const userName = channelName.substring(1);
logger.debug(`authenticateChannelCredentials > channelName: ${channelName} username: ${userName} pass: ${userPassword}`); logger.debug(`authenticateChannelCredentials > channelName: ${channelName} username: ${userName} pass: ${userPassword}`);
db.User db.User
@ -18,17 +20,23 @@ module.exports = {
resolve(false); resolve(false);
return; return;
} }
if (!user.validPassword(userPassword, user.password)) { return user.comparePassword(userPassword, (passwordErr, isMatch) => {
logger.debug('incorrect password'); if (passwordErr) {
resolve(false); logger.error('comparePassword error:', passwordErr);
return; resolve(false);
} return;
logger.debug('user found:', user.dataValues); }
resolve(true); if (!isMatch) {
logger.debug('incorrect password');
resolve(false);
return;
}
logger.debug('...password was a match...');
resolve(true);
});
}) })
.catch(error => { .catch(error => {
logger.error(error); reject(error);
reject();
}); });
}); });
}, },

View file

@ -13,17 +13,16 @@ module.exports = {
.then(tx => { .then(tx => {
logger.info(`Successfully published ${fileName}`, tx); logger.info(`Successfully published ${fileName}`, tx);
publishResults = tx; publishResults = tx;
return db.Channel.findOne({where: {channelName: publishParams.channel_name}}); // note: should this be db.User ?? if (publishParams.channel_name) {
}) logger.debug(`this claim was published in channel: ${publishParams.channel_name}`);
.then(channel => { return db.Channel.findOne({where: {channelName: publishParams.channel_name}}).then(channel => { return channel.channelClaimId });
let certificateId;
if (channel) {
certificateId = channel.channelClaimId;
logger.debug('successfully found channel in Channel table');
} else { } else {
certificateId = null; logger.debug('this claim was published in channel: n/a');
logger.debug('channel for publish not found in Channel table'); return null;
}; }
})
.then(certificateId => {
logger.debug(`certificateId: ${certificateId}`);
const fileRecord = { const fileRecord = {
name : publishParams.name, name : publishParams.name,
claimId : publishResults.claim_id, claimId : publishResults.claim_id,

View file

@ -3,11 +3,12 @@ const { postToStats } = require('../controllers/statsController.js');
module.exports = { module.exports = {
returnErrorMessageAndStatus: function (error) { returnErrorMessageAndStatus: function (error) {
let status; let status, message;
let message; // check for daemon being turned off
if (error.code === 'ECONNREFUSED') { if (error.code === 'ECONNREFUSED') {
status = 503; status = 503;
message = 'Connection refused. The daemon may not be running.'; message = 'Connection refused. The daemon may not be running.';
// check for errors from the deamon
} else if (error.response) { } else if (error.response) {
status = error.response.status || 500; status = error.response.status || 500;
if (error.response.data) { if (error.response.data) {
@ -21,7 +22,13 @@ module.exports = {
} else { } else {
message = error.response; message = error.response;
} }
// check for spee.ch thrown errors
} else if (error.message) {
status = 400;
message = error.message;
// fallback for everything else
} else { } else {
status = 400;
message = error; message = error;
} }
return [status, message]; return [status, message];

View file

@ -18,11 +18,10 @@ module.exports = {
throw new Error('no file with key of [file] found in request'); throw new Error('no file with key of [file] found in request');
} }
}, },
validatePublishSubmission (file, claimName, nsfw) { validatePublishSubmission (file, claimName) {
try { try {
module.exports.validateFile(file); module.exports.validateFile(file);
module.exports.validateClaimName(claimName); module.exports.validateClaimName(claimName);
module.exports.validateNSFW(nsfw);
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -76,24 +75,6 @@ module.exports = {
throw new Error('Only posts with a "Public Domain" or "Creative Commons" license are eligible for publishing through spee.ch'); throw new Error('Only posts with a "Public Domain" or "Creative Commons" license are eligible for publishing through spee.ch');
} }
}, },
cleanseNSFW (nsfw) {
switch (nsfw) {
case true:
case 'on':
case 'true':
case 1:
case '1':
return true;
case false:
case 'false':
case 'off':
case 0:
case '0':
return false;
default:
return false;
}
},
cleanseChannelName (channelName) { cleanseChannelName (channelName) {
if (channelName) { if (channelName) {
if (channelName.indexOf('@') !== 0) { if (channelName.indexOf('@') !== 0) {
@ -102,12 +83,6 @@ module.exports = {
} }
return channelName; return channelName;
}, },
validateNSFW (nsfw) {
if (nsfw === true || nsfw === false) {
return;
}
throw new Error('NSFW must be set to either true or false');
},
createPublishParams (filePath, name, title, description, license, nsfw, thumbnail, channelName) { createPublishParams (filePath, name, title, description, license, nsfw, thumbnail, channelName) {
logger.debug(`Creating Publish Parameters`); logger.debug(`Creating Publish Parameters`);
// provide defaults for title // provide defaults for title

View file

@ -64,20 +64,12 @@ var publishFileFunctions = {
} }
}, },
returnNullOrChannel: function () { returnNullOrChannel: function () {
const channelInput = document.getElementById('channel-name-select'); const channelRadio = document.getElementById('channel-radio');
const radios = document.getElementsByName('anonymous-or-channel'); if (channelRadio.checked) {
let anonymousOrInChannel; const channelInput = document.getElementById('channel-name-select');
// replace channelName with 'anonymous' if appropriate return channelInput.value.trim();
for (let i = 0; i < radios.length; i++) {
if (radios[i].checked) {
anonymousOrInChannel = radios[i].value; // do whatever you want with the checked radio
break; // only one radio can be logically checked, don't check the rest
}
} }
if (anonymousOrInChannel === 'anonymous') { return null;
return null;
};
return channelInput.value.trim();
}, },
createMetadata: function() { createMetadata: function() {
const nameInput = document.getElementById('claim-name-input'); const nameInput = document.getElementById('claim-name-input');
@ -89,13 +81,13 @@ var publishFileFunctions = {
return { return {
name: nameInput.value.trim(), name: nameInput.value.trim(),
channel: this.returnNullOrChannel(), channelName: this.returnNullOrChannel(),
title: titleInput.value.trim(), title: titleInput.value.trim(),
description: descriptionInput.value.trim(), description: descriptionInput.value.trim(),
license: licenseInput.value.trim(), license: licenseInput.value.trim(),
nsfw: nsfwInput.checked, nsfw: nsfwInput.checked,
type: stagedFiles[0].type, type: stagedFiles[0].type,
thumbnail: thumbnailInput.value.trim() thumbnail: thumbnailInput.value.trim(),
} }
}, },
appendDataToFormData: function (file, metadata) { appendDataToFormData: function (file, metadata) {

View file

@ -4,7 +4,7 @@ const multipartMiddleware = multipart();
const db = require('../models'); const db = require('../models');
const { publish } = require('../controllers/publishController.js'); const { publish } = require('../controllers/publishController.js');
const { getClaimList, resolveUri } = require('../helpers/lbryApi.js'); const { getClaimList, resolveUri } = require('../helpers/lbryApi.js');
const { createPublishParams, validateApiPublishRequest, validatePublishSubmission, cleanseNSFW, cleanseChannelName, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js'); const { createPublishParams, validateApiPublishRequest, validatePublishSubmission, cleanseChannelName, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js');
const errorHandlers = require('../helpers/errorHandlers.js'); const errorHandlers = require('../helpers/errorHandlers.js');
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
const { authenticateChannelCredentials } = require('../auth/authentication.js'); const { authenticateChannelCredentials } = require('../auth/authentication.js');
@ -71,7 +71,9 @@ module.exports = (app) => {
}); });
}); });
// route to run a publish request on the daemon // route to run a publish request on the daemon
app.post('/api/publish', multipartMiddleware, ({ body, files, ip, originalUrl }, res) => { app.post('/api/publish', multipartMiddleware, ({ body, files, ip, originalUrl, user }, res) => {
logger.debug('api/publish body:', body);
let file, fileName, filePath, fileType, name, nsfw, license, title, description, thumbnail, anonymous, skipAuth, channelName, channelPassword;
// validate that mandatory parts of the request are present // validate that mandatory parts of the request are present
try { try {
validateApiPublishRequest(body, files); validateApiPublishRequest(body, files);
@ -81,12 +83,12 @@ module.exports = (app) => {
return; return;
} }
// validate file, name, license, and nsfw // validate file, name, license, and nsfw
const file = files.file; file = files.file;
const fileName = file.name; fileName = file.name;
const filePath = file.path; filePath = file.path;
const fileType = file.type; fileType = file.type;
const name = body.name; name = body.name;
const nsfw = cleanseNSFW(body.nsfw); // cleanse nsfw input nsfw = (body.nsfw === 'true');
try { try {
validatePublishSubmission(file, name, nsfw); validatePublishSubmission(file, name, nsfw);
} catch (error) { } catch (error) {
@ -94,18 +96,36 @@ module.exports = (app) => {
res.status(400).json({success: false, message: error.message}); res.status(400).json({success: false, message: error.message});
return; return;
} }
logger.debug(`name: ${name}, nsfw: ${nsfw}`);
// optional inputs // optional inputs
const license = body.license || null; license = body.license || null;
const title = body.title || null; title = body.title || null;
const description = body.description || null; description = body.description || null;
const thumbnail = body.thumbnail || null; thumbnail = body.thumbnail || null;
let channelName = body.channelName || null; anonymous = (body.channelName === 'null') || (body.channelName === undefined);
if (user) {
channelName = user.channelName || null;
} else {
channelName = body.channelName || null;
}
channelPassword = body.channelPassword || null;
skipAuth = false;
// case 1: publish from spee.ch, client logged in
if (user) {
skipAuth = true;
if (anonymous) {
channelName = null;
}
// case 2: publish from api or spee.ch, client not logged in
} else {
if (anonymous) {
skipAuth = true;
channelName = null;
}
}
channelName = cleanseChannelName(channelName); channelName = cleanseChannelName(channelName);
const channelPassword = body.channelPassword || null; logger.debug(`name: ${name}, license: ${license} title: "${title}" description: "${description}" channelName: "${channelName}" channelPassword: "${channelPassword}" nsfw: "${nsfw}"`);
logger.debug(`license: ${license} title: "${title}" description: "${description}" channelName: "${channelName}" channelPassword: "${channelPassword}" nsfw: "${nsfw}"`);
// check channel authorization // check channel authorization
authenticateChannelCredentials(channelName, channelPassword) authenticateChannelCredentials(skipAuth, channelName, channelPassword)
.then(result => { .then(result => {
if (!result) { if (!result) {
throw new Error('Authentication failed, you do not have access to that channel'); throw new Error('Authentication failed, you do not have access to that channel');

View file

@ -3,11 +3,11 @@
<div class="column column--10"> <div class="column column--10">
<form> <form>
<div class="column column--3 column--med-10"> <div class="column column--3 column--med-10">
<input type="radio" name="anonymous-or-channel" id="anonymous-select" class="input-radio" value="anonymous" {{#unless user}}checked {{/unless}} onchange="toggleChannel(event.target.value)"/> <input type="radio" name="anonymous-or-channel" id="anonymous-radio" class="input-radio" value="anonymous" {{#unless user}}checked {{/unless}} onchange="toggleChannel(event.target.value)"/>
<label class="label label--pointer" for="anonymous-select">Anonymous</label> <label class="label label--pointer" for="anonymous-radio">Anonymous</label>
</div><div class="column column--7 column--med-10"> </div><div class="column column--7 column--med-10">
<input type="radio" name="anonymous-or-channel" id="in-a-channel-select" class="input-radio" value="in a channel" {{#if user}}checked {{/if}} onchange="toggleChannel(event.target.value)"/> <input type="radio" name="anonymous-or-channel" id="channel-radio" class="input-radio" value="in a channel" {{#if user}}checked {{/if}} onchange="toggleChannel(event.target.value)"/>
<label class="label label--pointer" for="in-a-channel-select">In a channel</label> <label class="label label--pointer" for="channel-radio">In a channel</label>
</div> </div>
</form> </form>
</div> </div>