From d57a6b3340f19590a0541f52bddbe0bba533dd69 Mon Sep 17 00:00:00 2001 From: bill bittner Date: Sun, 29 Apr 2018 12:17:23 -0700 Subject: [PATCH] added check for blocked content and updated claimid flow to use rejected promises --- .../checkClaimAvailability.js | 2 +- .../api/claim/availability/index.js | 2 +- .../api/claim/longId/getClaimId.js | 31 +++++++ server/controllers/api/claim/longId/index.js | 24 ++++-- server/controllers/api/claim/publish/index.js | 2 +- .../assets/utils/getClaimIdAndServeAsset.js | 65 +++++++-------- .../{ => assets}/utils/getLocalFileRecord.js | 2 +- .../assets/utils/serveAssetToClient.js | 28 +++++++ server/controllers/utils/getClaimId.js | 57 ------------- server/models/blocked.js | 24 ++++-- server/models/certificate.js | 65 +++++++++------ server/models/claim.js | 82 +++++++++++-------- 12 files changed, 211 insertions(+), 173 deletions(-) rename server/controllers/api/claim/{utils => availability}/checkClaimAvailability.js (94%) create mode 100644 server/controllers/api/claim/longId/getClaimId.js rename server/controllers/{ => assets}/utils/getLocalFileRecord.js (87%) create mode 100644 server/controllers/assets/utils/serveAssetToClient.js delete mode 100644 server/controllers/utils/getClaimId.js diff --git a/server/controllers/api/claim/utils/checkClaimAvailability.js b/server/controllers/api/claim/availability/checkClaimAvailability.js similarity index 94% rename from server/controllers/api/claim/utils/checkClaimAvailability.js rename to server/controllers/api/claim/availability/checkClaimAvailability.js index f10f2590..c4f4501a 100644 --- a/server/controllers/api/claim/utils/checkClaimAvailability.js +++ b/server/controllers/api/claim/availability/checkClaimAvailability.js @@ -1,4 +1,4 @@ -const db = require('../../../../models'); +const db = require('../../../../models/index'); const { publishing: { primaryClaimAddress, additionalClaimAddresses } } = require('../../../../../config/siteConfig.js'); const Sequelize = require('sequelize'); const Op = Sequelize.Op; diff --git a/server/controllers/api/claim/availability/index.js b/server/controllers/api/claim/availability/index.js index 9f9d330d..1583cb26 100644 --- a/server/controllers/api/claim/availability/index.js +++ b/server/controllers/api/claim/availability/index.js @@ -1,4 +1,4 @@ -const checkClaimAvailability = require('../utils/checkClaimAvailability.js'); +const checkClaimAvailability = require('./checkClaimAvailability.js'); const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); diff --git a/server/controllers/api/claim/longId/getClaimId.js b/server/controllers/api/claim/longId/getClaimId.js new file mode 100644 index 00000000..d1a3a212 --- /dev/null +++ b/server/controllers/api/claim/longId/getClaimId.js @@ -0,0 +1,31 @@ +const logger = require('winston'); + +const db = require('../../../../models'); + +const getClaimIdByChannel = (channelName, channelClaimId, claimName) => { + return new Promise((resolve, reject) => { + db.Certificate + .getLongChannelId(channelName, channelClaimId) + .then(longChannelId => { + return db.Claim.getClaimIdByLongChannelId(longChannelId, claimName); + }) + .then(longClaimId => { + resolve(longClaimId); + }) + .catch(error => { + reject(error); + }); + }); +}; + +const getClaimId = (channelName, channelClaimId, name, claimId) => { + if (channelName) { + logger.debug(`getClaimIdByChannel(${channelName}, ${channelClaimId}, ${name})`); + return getClaimIdByChannel(channelName, channelClaimId, name); + } else { + logger.debug(`db.Claim.getLongClaimId(${name}, ${claimId})`); + return db.Claim.getLongClaimId(name, claimId); + } +}; + +module.exports = getClaimId; diff --git a/server/controllers/api/claim/longId/index.js b/server/controllers/api/claim/longId/index.js index f246fd0c..c9dd78be 100644 --- a/server/controllers/api/claim/longId/index.js +++ b/server/controllers/api/claim/longId/index.js @@ -1,4 +1,4 @@ -const getClaimId = require('../../../utils/getClaimId.js'); +const getClaimId = require('./getClaimId.js'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); const NO_CHANNEL = 'NO_CHANNEL'; @@ -16,16 +16,22 @@ const claimLongId = ({ ip, originalUrl, body, params }, res) => { const claimName = body.claimName; const claimId = body.claimId; getClaimId(channelName, channelClaimId, claimName, claimId) - .then(result => { - if (result === NO_CHANNEL) { - return res.status(404).json({success: false, message: 'No matching channel could be found'}); - } - if (result === NO_CLAIM) { - return res.status(404).json({success: false, message: 'No matching claim id could be found'}); - } - res.status(200).json({success: true, data: result}); + .then(fullClaimId => { + res.status(200).json({success: true, data: fullClaimId}); }) .catch(error => { + if (error === NO_CLAIM) { + return res.status(404).json({ + success: false, + message: 'No claim id could be found', + }); + } + if (error === NO_CHANNEL) { + return res.status(404).json({ + success: false, + message: 'No channel id could be found', + }); + } handleErrorResponse(originalUrl, ip, error, res); }); }; diff --git a/server/controllers/api/claim/publish/index.js b/server/controllers/api/claim/publish/index.js index 1e95d317..0a748753 100644 --- a/server/controllers/api/claim/publish/index.js +++ b/server/controllers/api/claim/publish/index.js @@ -4,7 +4,7 @@ const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); -const checkClaimAvailability = require('../utils/checkClaimAvailability.js'); +const checkClaimAvailability = require('../availability/checkClaimAvailability.js'); const publish = require('./publish.js'); const createBasicPublishParams = require('./createBasicPublishParams.js'); diff --git a/server/controllers/assets/utils/getClaimIdAndServeAsset.js b/server/controllers/assets/utils/getClaimIdAndServeAsset.js index f5c50a1f..6bd868b2 100644 --- a/server/controllers/assets/utils/getClaimIdAndServeAsset.js +++ b/server/controllers/assets/utils/getClaimIdAndServeAsset.js @@ -1,50 +1,43 @@ -const logger = require('winston'); -const getClaimId = require('../../utils/getClaimId.js'); -const getLocalFileRecord = require('../../utils/getLocalFileRecord.js'); +const db = require('../../../models'); + +const getClaimId = require('../../api/claim/longId/getClaimId.js'); const { handleErrorResponse } = require('../../utils/errorHandlers.js'); -const NO_FILE = 'NO_FILE'; +const serveAssetToClient = require('./serveAssetToClient.js'); + const NO_CHANNEL = 'NO_CHANNEL'; const NO_CLAIM = 'NO_CLAIM'; - -const serveAssetToClient = (claimId, name, res) => { - return getLocalFileRecord(claimId, name) - .then(fileRecord => { - // check that a local record was found - if (fileRecord === NO_FILE) { - return res.status(307).redirect(`/api/claim/get/${name}/${claimId}`); - } - // serve the file - const {filePath, fileType} = fileRecord; - logger.verbose(`serving file: ${filePath}`); - const sendFileOptions = { - headers: { - 'X-Content-Type-Options': 'nosniff', - 'Content-Type' : fileType || 'image/jpeg', - }, - }; - res.status(200).sendFile(filePath, sendFileOptions); - }) - .catch(error => { - throw error; - }); -}; +const BLOCKED_CLAIM = 'BLOCKED_CLAIM'; const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId, originalUrl, ip, res) => { - // get the claim Id and then serve the asset getClaimId(channelName, channelClaimId, claimName, claimId) .then(fullClaimId => { - if (fullClaimId === NO_CLAIM) { - return res.status(404).json({success: false, message: 'no claim id could be found'}); - } else if (fullClaimId === NO_CHANNEL) { - return res.status(404).json({success: false, message: 'no channel id could be found'}); - } - serveAssetToClient(fullClaimId, claimName, res); - // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'success'); + claimId = fullClaimId; + return db.Blocked.isNotBlocked(fullClaimId, claimName); + }) + .then(() => { + serveAssetToClient(claimId, claimName, res); }) .catch(error => { + if (error === NO_CLAIM) { + return res.status(404).json({ + success: false, + message: 'No claim id could be found', + }); + } + if (error === NO_CHANNEL) { + return res.status(404).json({ + success: false, + message: 'No channel id could be found', + }); + } + if (error === BLOCKED_CLAIM) { + return res.status(410).json({ + success: false, + message: 'In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications. For more details, see https://lbry.io/faq/dmca', + }); + } handleErrorResponse(originalUrl, ip, error, res); - // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'fail'); }); }; diff --git a/server/controllers/utils/getLocalFileRecord.js b/server/controllers/assets/utils/getLocalFileRecord.js similarity index 87% rename from server/controllers/utils/getLocalFileRecord.js rename to server/controllers/assets/utils/getLocalFileRecord.js index 3d2016d1..53f8a469 100644 --- a/server/controllers/utils/getLocalFileRecord.js +++ b/server/controllers/assets/utils/getLocalFileRecord.js @@ -1,4 +1,4 @@ -const db = require('../../models'); +const db = require('../../../models'); const NO_FILE = 'NO_FILE'; diff --git a/server/controllers/assets/utils/serveAssetToClient.js b/server/controllers/assets/utils/serveAssetToClient.js new file mode 100644 index 00000000..28991d24 --- /dev/null +++ b/server/controllers/assets/utils/serveAssetToClient.js @@ -0,0 +1,28 @@ +const logger = require('winston'); +const getLocalFileRecord = require('./getLocalFileRecord.js'); +const NO_FILE = 'NO_FILE'; + +const serveAssetToClient = (claimId, name, res) => { + return getLocalFileRecord(claimId, name) + .then(fileRecord => { + // check that a local record was found + if (fileRecord === NO_FILE) { + return res.status(307).redirect(`/api/claim/get/${name}/${claimId}`); + } + // serve the file + const {filePath, fileType} = fileRecord; + logger.verbose(`serving file: ${filePath}`); + const sendFileOptions = { + headers: { + 'X-Content-Type-Options': 'nosniff', + 'Content-Type' : fileType || 'image/jpeg', + }, + }; + res.status(200).sendFile(filePath, sendFileOptions); + }) + .catch(error => { + throw error; + }); +}; + +module.exports = serveAssetToClient; diff --git a/server/controllers/utils/getClaimId.js b/server/controllers/utils/getClaimId.js deleted file mode 100644 index 4e6c5a29..00000000 --- a/server/controllers/utils/getClaimId.js +++ /dev/null @@ -1,57 +0,0 @@ -const logger = require('winston'); - -const db = require('../../models'); - -const NO_CHANNEL = 'NO_CHANNEL'; -const NO_CLAIM = 'NO_CLAIM'; - -const getClaimIdByClaim = (claimName, claimId) => { - logger.debug(`getClaimIdByClaim(${claimName}, ${claimId})`); - return new Promise((resolve, reject) => { - db.Claim.getLongClaimId(claimName, claimId) - .then(longClaimId => { - if (!longClaimId) { - resolve(NO_CLAIM); - } - resolve(longClaimId); - }) - .catch(error => { - reject(error); - }); - }); -}; - -const getClaimIdByChannel = (channelName, channelClaimId, claimName) => { - logger.debug(`getClaimIdByChannel(${channelName}, ${channelClaimId}, ${claimName})`); - return new Promise((resolve, reject) => { - db.Certificate.getLongChannelId(channelName, channelClaimId) // 1. get the long channel id - .then(longChannelId => { - if (!longChannelId) { - return [null, null]; - } - return Promise.all([longChannelId, db.Claim.getClaimIdByLongChannelId(longChannelId, claimName)]); // 2. get the long claim id - }) - .then(([longChannelId, longClaimId]) => { - if (!longChannelId) { - return resolve(NO_CHANNEL); - } - if (!longClaimId) { - return resolve(NO_CLAIM); - } - resolve(longClaimId); - }) - .catch(error => { - reject(error); - }); - }); -}; - -const getClaimId = (channelName, channelClaimId, name, claimId) => { - if (channelName) { - return getClaimIdByChannel(channelName, channelClaimId, name); - } else { - return getClaimIdByClaim(name, claimId); - } -}; - -module.exports = getClaimId; diff --git a/server/models/blocked.js b/server/models/blocked.js index a3955c47..12a04ca9 100644 --- a/server/models/blocked.js +++ b/server/models/blocked.js @@ -1,9 +1,19 @@ const logger = require('winston'); +const BLOCKED_CLAIM = 'BLOCKED_CLAIM'; + module.exports = (sequelize, { STRING }) => { const Blocked = sequelize.define( 'Blocked', { + claimId: { + type : STRING, + allowNull: false, + }, + name: { + type : STRING, + allowNull: false, + }, outpoint: { type : STRING, allowNull: false, @@ -14,22 +24,24 @@ module.exports = (sequelize, { STRING }) => { } ); - Blocked.isBlocked = function (outpoint) { - logger.debug(`checking to see if ${outpoint} is blocked`); + Blocked.isNotBlocked = function (claimId, name) { + logger.debug(`checking to see if ${name}#${claimId} is not blocked`); return new Promise((resolve, reject) => { this.findOne({ where: { - outpoint, + claimId, + name, }, }) .then(result => { - if (!result) { - return resolve(false); + if (result) { + return reject(BLOCKED_CLAIM); } resolve(true); }) .catch(error => { - reject(error); + logger.error(error); + reject(BLOCKED_CLAIM); }); }); }; diff --git a/server/models/certificate.js b/server/models/certificate.js index 679f6312..85a5bc50 100644 --- a/server/models/certificate.js +++ b/server/models/certificate.js @@ -1,6 +1,16 @@ const logger = require('winston'); const returnShortId = require('./utils/returnShortId.js'); +const NO_CHANNEL = 'NO_CHANNEL'; + +function isLongChannelId (channelId) { + return (channelId && (channelId.length === 40)); +} + +function isShortChannelId (channelId) { + return (channelId && (channelId.length < 40)); +} + module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { const Certificate = sequelize.define( 'Certificate', @@ -121,6 +131,25 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { }); }; + Certificate.validateLongChannelId = function (name, claimId) { + logger.debug(`validateLongChannelId(${name}, ${claimId})`); + return new Promise((resolve, reject) => { + this.findOne({ + where: {name, claimId}, + }) + .then(result => { + if (!result) { + return reject(NO_CHANNEL); + } + resolve(claimId); + }) + .catch(error => { + logger.error(error); + reject(NO_CHANNEL); + }); + }); + }; + Certificate.getLongChannelIdFromShortChannelId = function (channelName, channelClaimId) { logger.debug(`getLongChannelIdFromShortChannelId(${channelName}, ${channelClaimId})`); return new Promise((resolve, reject) => { @@ -137,13 +166,14 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { .then(result => { switch (result.length) { case 0: - return resolve(null); - default: // note results must be sorted + return reject(NO_CHANNEL); + default: return resolve(result[0].claimId); } }) .catch(error => { - reject(error); + logger.error(error); + reject(NO_CHANNEL); }); }); }; @@ -159,43 +189,26 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { .then(result => { switch (result.length) { case 0: - return resolve(null); + return reject(NO_CHANNEL); default: return resolve(result[0].claimId); } }) .catch(error => { - reject(error); - }); - }); - }; - - Certificate.validateLongChannelId = function (name, claimId) { - logger.debug(`validateLongChannelId(${name}, ${claimId})`); - return new Promise((resolve, reject) => { - this.findOne({ - where: {name, claimId}, - }) - .then(result => { - if (!result) { - return resolve(null); - }; - resolve(claimId); - }) - .catch(error => { - reject(error); + logger.error(error); + reject(NO_CHANNEL); }); }); }; Certificate.getLongChannelId = function (channelName, channelClaimId) { logger.debug(`getLongChannelId(${channelName}, ${channelClaimId})`); - if (channelClaimId && (channelClaimId.length === 40)) { // if a full channel id is provided + if (isLongChannelId(channelClaimId)) { return this.validateLongChannelId(channelName, channelClaimId); - } else if (channelClaimId && channelClaimId.length < 40) { // if a short channel id is provided + } else if (isShortChannelId(channelClaimId)) { return this.getLongChannelIdFromShortChannelId(channelName, channelClaimId); } else { - return this.getLongChannelIdFromChannelName(channelName); // if no channel id provided + return this.getLongChannelIdFromChannelName(channelName); } }; diff --git a/server/models/claim.js b/server/models/claim.js index a5232e6d..409ad94c 100644 --- a/server/models/claim.js +++ b/server/models/claim.js @@ -2,6 +2,8 @@ const logger = require('winston'); const returnShortId = require('./utils/returnShortId.js'); const { assetDefaults: { thumbnail: defaultThumbnail }, details: { host } } = require('../../config/siteConfig.js'); +const NO_CLAIM = 'NO_CLAIM'; + function determineFileExtensionFromContentType (contentType) { switch (contentType) { case 'image/jpeg': @@ -17,14 +19,14 @@ function determineFileExtensionFromContentType (contentType) { logger.debug('setting unknown file type as file extension jpeg'); return 'jpeg'; } -}; +} function determineThumbnail (storedThumbnail, defaultThumbnail) { if (storedThumbnail === '') { return defaultThumbnail; } return storedThumbnail; -}; +} function prepareClaimData (claim) { // logger.debug('preparing claim data based on resolved data:', claim); @@ -32,7 +34,15 @@ function prepareClaimData (claim) { claim['fileExt'] = determineFileExtensionFromContentType(claim.contentType); claim['host'] = host; return claim; -}; +} + +function isLongClaimId (claimId) { + return (claimId && (claimId.length === 40)); +} + +function isShortClaimId (claimId) { + return (claimId && (claimId.length < 40)); +} module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { const Claim = sequelize.define( @@ -265,6 +275,27 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { }); }; + Claim.validateLongClaimId = function (name, claimId) { + return new Promise((resolve, reject) => { + this.findOne({ + where: { + name, + claimId, + }, + }) + .then(result => { + if (!result) { + return reject(NO_CLAIM); + } + resolve(claimId); + }) + .catch(error => { + logger.error(error); + reject(NO_CLAIM); + }); + }); + }; + Claim.getLongClaimIdFromShortClaimId = function (name, shortId) { return new Promise((resolve, reject) => { this @@ -279,13 +310,14 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { .then(result => { switch (result.length) { case 0: - return resolve(null); - default: // note results must be sorted + return reject(NO_CLAIM); + default: return resolve(result[0].claimId); } }) .catch(error => { - reject(error); + logger.error(error); + reject(NO_CLAIM); }); }); }; @@ -295,51 +327,31 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { this .findAll({ where: { name }, - order: [['effectiveAmount', 'DESC'], ['height', 'ASC']], // note: maybe height and effective amount need to switch? + order: [['effectiveAmount', 'DESC'], ['height', 'ASC']], }) .then(result => { - logger.debug('length of result', result.length); switch (result.length) { case 0: - return resolve(null); + return reject(NO_CLAIM); default: return resolve(result[0].dataValues.claimId); } }) .catch(error => { - reject(error); - }); - }); - }; - - Claim.validateLongClaimId = function (name, claimId) { - return new Promise((resolve, reject) => { - this.findOne({ - where: { - name, - claimId, - }, - }) - .then(result => { - if (!result) { - return resolve(null); - }; - resolve(claimId); - }) - .catch(error => { - reject(error); + logger.error(error); + reject(NO_CLAIM); }); }); }; Claim.getLongClaimId = function (claimName, claimId) { - logger.debug(`getLongClaimId(${claimName}, ${claimId})`); - if (claimId && (claimId.length === 40)) { // if a full claim id is provided + // logger.debug(`getLongClaimId(${claimName}, ${claimId})`); + if (isLongClaimId(claimId)) { return this.validateLongClaimId(claimName, claimId); - } else if (claimId && claimId.length < 40) { - return this.getLongClaimIdFromShortClaimId(claimName, claimId); // if a short claim id is provided + } else if (isShortClaimId(claimId)) { + return this.getLongClaimIdFromShortClaimId(claimName, claimId); } else { - return this.getTopFreeClaimIdByClaimName(claimName); // if no claim id is provided + return this.getTopFreeClaimIdByClaimName(claimName); } };