From 1358215e6fa7249c2c830a54fa7337973f410b1c Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 17 Sep 2018 09:01:46 -0400 Subject: [PATCH 01/15] reject requests for content from unapproved channels when serveApprovedOnly --- server/controllers/api/channel/data/index.js | 7 +++++ .../assets/utils/getClaimIdAndServeAsset.js | 26 ++++++++++++++++--- server/models/claim.js | 5 ++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/server/controllers/api/channel/data/index.js b/server/controllers/api/channel/data/index.js index 610f1cb5..c9ceebf4 100644 --- a/server/controllers/api/channel/data/index.js +++ b/server/controllers/api/channel/data/index.js @@ -1,6 +1,7 @@ const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); const getChannelData = require('./getChannelData.js'); +const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig'); const NO_CHANNEL = 'NO_CHANNEL'; @@ -14,6 +15,12 @@ const channelData = ({ ip, originalUrl, body, params }, res) => { const channelName = params.channelName; let channelClaimId = params.channelClaimId; if (channelClaimId === 'none') channelClaimId = null; + if (serveOnlyApproved && approvedChannels && !approvedChannels.includes(channelClaimId)) { + return res.status(404).json({ + success: false, + message: 'This spee.ch instance serves limited content which does not include this asset', + }); + } getChannelData(channelName, channelClaimId) .then(data => { res.status(200).json({ diff --git a/server/controllers/assets/utils/getClaimIdAndServeAsset.js b/server/controllers/assets/utils/getClaimIdAndServeAsset.js index 00bf9532..66486ba8 100644 --- a/server/controllers/assets/utils/getClaimIdAndServeAsset.js +++ b/server/controllers/assets/utils/getClaimIdAndServeAsset.js @@ -11,17 +11,28 @@ const NO_CHANNEL = 'NO_CHANNEL'; const NO_CLAIM = 'NO_CLAIM'; const BLOCKED_CLAIM = 'BLOCKED_CLAIM'; const NO_FILE = 'NO_FILE'; +const UNAPPROVED_CHANNEL = 'UNAPPROVED_CHANNEL'; + +const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig'); const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId, originalUrl, ip, res) => { getClaimId(channelName, channelClaimId, claimName, claimId) .then(fullClaimId => { claimId = fullClaimId; logger.debug('Full claim id:', fullClaimId); - return db.Claim.getOutpoint(claimName, fullClaimId); + return db.Claim.findOne({ + where: { + name : claimName, + claimId: fullClaimId, + }, + }); }) - .then(outpoint => { - logger.debug('Outpoint:', outpoint); - return db.Blocked.isNotBlocked(outpoint); + .then(claim => { + if (serveOnlyApproved && !approvedChannels.includes(claim.dataValues.certificateId)) { + throw new Error(UNAPPROVED_CHANNEL); + } + logger.debug('Outpoint:', claim.dataValues.outpoint); + return db.Blocked.isNotBlocked(claim.dataValues.outpoint); }) .then(() => { return db.File.findOne({ @@ -52,6 +63,13 @@ const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId message: 'No matching channel id could be found for that url', }); } + if (error === UNAPPROVED_CHANNEL) { + logger.debug('unapproved channel'); + return res.status(400).json({ + success: false, + message: 'This spee.ch instance serves limited content which does not include this asset', + }); + } if (error === BLOCKED_CLAIM) { logger.debug('claim was blocked'); return res.status(451).json({ diff --git a/server/models/claim.js b/server/models/claim.js index 6c1f4092..55010987 100644 --- a/server/models/claim.js +++ b/server/models/claim.js @@ -1,8 +1,10 @@ const logger = require('winston'); const returnShortId = require('./utils/returnShortId.js'); const { assetDefaults: { thumbnail: defaultThumbnail }, details: { host } } = require('@config/siteConfig'); +const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig'); const NO_CLAIM = 'NO_CLAIM'; +const NOT_ALLOWED = 'NOT_ALLOWED'; function determineFileExtensionFromContentType (contentType) { switch (contentType) { @@ -362,6 +364,9 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { where: { name, claimId }, }) .then(claimArray => { + if (serveOnlyApproved && !approvedChannels.includes(claimArray[0].dataValues.certificateId)) { + reject(NOT_ALLOWED); + } switch (claimArray.length) { case 0: return resolve(null); From 815149a2cd06574fccb4278dc7b8e1394cc71f53 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Wed, 19 Sep 2018 13:27:55 -0400 Subject: [PATCH 02/15] reject publishes for unapproved channels when publishOnlyApproved --- server/controllers/api/claim/publish/index.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/server/controllers/api/claim/publish/index.js b/server/controllers/api/claim/publish/index.js index d636de8d..4f551053 100644 --- a/server/controllers/api/claim/publish/index.js +++ b/server/controllers/api/claim/publish/index.js @@ -3,6 +3,7 @@ const logger = require('winston'); const { details: { host }, publishing: { disabled, disabledMessage } } = require('@config/siteConfig'); const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js'); +const { publishing: { publishOnlyApproved, approvedChannels } } = require('@config/siteConfig'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); @@ -16,6 +17,7 @@ const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js'); const authenticateUser = require('./authentication.js'); const CLAIM_TAKEN = 'CLAIM_TAKEN'; +const UNAPPROVED_CHANNEL = 'UNAPPROVED_CHANNEL'; /* @@ -54,6 +56,13 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) // check channel authorization authenticateUser(channelName, channelId, channelPassword, user) .then(({ channelName, channelClaimId }) => { + if (publishOnlyApproved && approvedChannels && !approvedChannels.includes(channelClaimId)) { + const error = { + name : UNAPPROVED_CHANNEL, + message: 'This spee.ch instance only allows publishing to approved channels', + }; + throw error; + } return Promise.all([ checkClaimAvailability(name), createPublishParams(filePath, name, title, description, license, nsfw, thumbnail, channelName, channelClaimId), @@ -98,6 +107,12 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) message: error.message, }); } + if (error.name === UNAPPROVED_CHANNEL) { + res.status(400).json({ + success: false, + message: error.message, + }); + } handleErrorResponse(originalUrl, ip, error, res); }); }; From 845a0a3b0131d638603a3e07fda60fe03be534be Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Wed, 19 Sep 2018 15:14:20 -0400 Subject: [PATCH 03/15] disable signup when closedRegistration --- server/speechPassport/utils/local-signup.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/speechPassport/utils/local-signup.js b/server/speechPassport/utils/local-signup.js index 1a2067e8..2662b721 100644 --- a/server/speechPassport/utils/local-signup.js +++ b/server/speechPassport/utils/local-signup.js @@ -2,6 +2,7 @@ const PassportLocalStrategy = require('passport-local').Strategy; const { createChannel } = require('../../lbrynet'); const logger = require('winston'); const db = require('../../models'); +const { publishing: { closedRegistration } } = require('@config/siteConfig'); module.exports = new PassportLocalStrategy( { @@ -9,10 +10,13 @@ module.exports = new PassportLocalStrategy( passwordField: 'password', }, (username, password, done) => { + if (closedRegistration) { + return done('Registration is disabled'); + } + logger.verbose(`new channel signup request. user: ${username} pass: ${password} .`); let userInfo = {}; // server-side validaton of inputs (username, password) - // create the channel and retrieve the metadata return createChannel(`@${username}`) .then(tx => { From 3c21ce36b7dac2b3248085f07557cb1fc8e9cf96 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Wed, 19 Sep 2018 16:35:10 -0400 Subject: [PATCH 04/15] hide unusable components based on site settings --- client/src/components/ChannelTools/index.jsx | 21 -------------- client/src/containers/ChannelTools/index.js | 10 +++++++ client/src/containers/ChannelTools/view.jsx | 23 +++++++++++++++ .../src/containers/NavigationLinks/index.jsx | 3 +- .../src/containers/NavigationLinks/view.jsx | 9 +++--- client/src/pages/LoginPage/view.jsx | 2 +- client/src/reducers/site.js | 29 ++++++++++++++----- 7 files changed, 63 insertions(+), 34 deletions(-) delete mode 100644 client/src/components/ChannelTools/index.jsx create mode 100644 client/src/containers/ChannelTools/index.js create mode 100644 client/src/containers/ChannelTools/view.jsx diff --git a/client/src/components/ChannelTools/index.jsx b/client/src/components/ChannelTools/index.jsx deleted file mode 100644 index 5e640dd9..00000000 --- a/client/src/components/ChannelTools/index.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import ChannelLoginForm from '@containers/ChannelLoginForm'; -import ChannelCreateForm from '@containers/ChannelCreateForm'; -import Row from '@components/Row'; - -const ChannelTools = () => { - return ( -
- -

Log in to an existing channel:

- -
- -

Create a brand new channel:

- -
-
- ); -}; - -export default ChannelTools; diff --git a/client/src/containers/ChannelTools/index.js b/client/src/containers/ChannelTools/index.js new file mode 100644 index 00000000..25b68fbc --- /dev/null +++ b/client/src/containers/ChannelTools/index.js @@ -0,0 +1,10 @@ +import { connect } from 'react-redux'; +import View from './view'; + +const mapStateToProps = ({ site: { closedRegistration } }) => { + return { + closedRegistration, + }; +}; + +export default connect(mapStateToProps, null)(View); diff --git a/client/src/containers/ChannelTools/view.jsx b/client/src/containers/ChannelTools/view.jsx new file mode 100644 index 00000000..0be7bef0 --- /dev/null +++ b/client/src/containers/ChannelTools/view.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import ChannelLoginForm from '@containers/ChannelLoginForm'; +import ChannelCreateForm from '@containers/ChannelCreateForm'; +import Row from '@components/Row'; + +class ChannelTools extends React.Component { + render () { + return ( +
+ +

Log in to an existing channel:

+ +
+ {!this.props.closedRegistration && ( +

Create a brand new channel:

+ +
)} +
+ ); + } +} + +export default ChannelTools; diff --git a/client/src/containers/NavigationLinks/index.jsx b/client/src/containers/NavigationLinks/index.jsx index 264bb027..7ef8ff85 100644 --- a/client/src/containers/NavigationLinks/index.jsx +++ b/client/src/containers/NavigationLinks/index.jsx @@ -2,8 +2,9 @@ import { connect } from 'react-redux'; import { logOutChannel, checkForLoggedInChannel } from '../../actions/channel'; import View from './view'; -const mapStateToProps = ({ channel: { loggedInChannel: { name, shortId, longId } } }) => { +const mapStateToProps = ({ site, channel: { loggedInChannel: { name, shortId, longId } } }) => { return { + site, channelName : name, channelShortId: shortId, channelLongId : longId, diff --git a/client/src/containers/NavigationLinks/view.jsx b/client/src/containers/NavigationLinks/view.jsx index 929006d1..3876702e 100644 --- a/client/src/containers/NavigationLinks/view.jsx +++ b/client/src/containers/NavigationLinks/view.jsx @@ -28,16 +28,17 @@ class NavigationLinks extends React.Component { } } render () { + const { site, channelLongId, channelName } = this.props; return (
- Publish - + } About - { this.props.channelName ? ( + { channelName ? ( - ) : ( + ) : !site.closedRegistration && ( Date: Thu, 20 Sep 2018 13:34:53 -0400 Subject: [PATCH 05/15] refactor channel checks with isApprovedChannel --- client/src/containers/NavigationLinks/view.jsx | 3 ++- client/src/utils/isApprovedChannel.js | 9 +++++++++ server/controllers/api/channel/data/index.js | 12 ++++++++---- server/controllers/api/claim/publish/index.js | 5 +++-- .../assets/utils/getClaimIdAndServeAsset.js | 5 +++-- server/models/claim.js | 5 +++-- server/utils/isApprovedChannel.js | 11 +++++++++++ 7 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 client/src/utils/isApprovedChannel.js create mode 100644 server/utils/isApprovedChannel.js diff --git a/client/src/containers/NavigationLinks/view.jsx b/client/src/containers/NavigationLinks/view.jsx index 3876702e..2f8ce8ca 100644 --- a/client/src/containers/NavigationLinks/view.jsx +++ b/client/src/containers/NavigationLinks/view.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { NavLink, withRouter } from 'react-router-dom'; import NavBarChannelOptionsDropdown from '@components/NavBarChannelOptionsDropdown'; +import isApprovedChannel from '../../utils/isApprovedChannel'; const VIEW = 'VIEW'; const LOGOUT = 'LOGOUT'; @@ -31,7 +32,7 @@ class NavigationLinks extends React.Component { const { site, channelLongId, channelName } = this.props; return (
- {(!site.publishOnlyApproved || site.approvedChannels.includes(channelLongId)) && chan.longId === long)) || + (name && short && channels.find(chan => chan.name === name && chan.shortId === short)) + ); +} + +module.exports = isApprovedChannel; diff --git a/server/controllers/api/channel/data/index.js b/server/controllers/api/channel/data/index.js index c9ceebf4..440326b5 100644 --- a/server/controllers/api/channel/data/index.js +++ b/server/controllers/api/channel/data/index.js @@ -1,8 +1,7 @@ const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); - const getChannelData = require('./getChannelData.js'); -const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig'); - +const isApprovedChannel = require('../../../../utils/isApprovedChannel'); +const { publishing: { serveOnlyApproved } } = require('@config/siteConfig'); const NO_CHANNEL = 'NO_CHANNEL'; /* @@ -15,12 +14,17 @@ const channelData = ({ ip, originalUrl, body, params }, res) => { const channelName = params.channelName; let channelClaimId = params.channelClaimId; if (channelClaimId === 'none') channelClaimId = null; - if (serveOnlyApproved && approvedChannels && !approvedChannels.includes(channelClaimId)) { + + const chanObj = {}; + if (channelName) chanObj.name = channelName; + if (channelClaimId) chanObj[(channelClaimId.length === 40 ? 'longId' : 'shortId')] = channelClaimId; + if (serveOnlyApproved && !isApprovedChannel(chanObj)) { return res.status(404).json({ success: false, message: 'This spee.ch instance serves limited content which does not include this asset', }); } + getChannelData(channelName, channelClaimId) .then(data => { res.status(200).json({ diff --git a/server/controllers/api/claim/publish/index.js b/server/controllers/api/claim/publish/index.js index 4f551053..c5a2bdbb 100644 --- a/server/controllers/api/claim/publish/index.js +++ b/server/controllers/api/claim/publish/index.js @@ -3,7 +3,8 @@ const logger = require('winston'); const { details: { host }, publishing: { disabled, disabledMessage } } = require('@config/siteConfig'); const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js'); -const { publishing: { publishOnlyApproved, approvedChannels } } = require('@config/siteConfig'); +const isApprovedChannel = require('../../../../utils/isApprovedChannel'); +const { publishing: { publishOnlyApproved } } = require('@config/siteConfig'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); @@ -56,7 +57,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) // check channel authorization authenticateUser(channelName, channelId, channelPassword, user) .then(({ channelName, channelClaimId }) => { - if (publishOnlyApproved && approvedChannels && !approvedChannels.includes(channelClaimId)) { + if (publishOnlyApproved && !isApprovedChannel({ longId: channelClaimId })) { const error = { name : UNAPPROVED_CHANNEL, message: 'This spee.ch instance only allows publishing to approved channels', diff --git a/server/controllers/assets/utils/getClaimIdAndServeAsset.js b/server/controllers/assets/utils/getClaimIdAndServeAsset.js index 66486ba8..af81162c 100644 --- a/server/controllers/assets/utils/getClaimIdAndServeAsset.js +++ b/server/controllers/assets/utils/getClaimIdAndServeAsset.js @@ -1,6 +1,7 @@ const logger = require('winston'); const db = require('../../../models'); +const isApprovedChannel = require('../../../utils/isApprovedChannel'); const getClaimId = require('../../utils/getClaimId.js'); const { handleErrorResponse } = require('../../utils/errorHandlers.js'); @@ -13,7 +14,7 @@ const BLOCKED_CLAIM = 'BLOCKED_CLAIM'; const NO_FILE = 'NO_FILE'; const UNAPPROVED_CHANNEL = 'UNAPPROVED_CHANNEL'; -const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig'); +const { publishing: { serveOnlyApproved } } = require('@config/siteConfig'); const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId, originalUrl, ip, res) => { getClaimId(channelName, channelClaimId, claimName, claimId) @@ -28,7 +29,7 @@ const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId }); }) .then(claim => { - if (serveOnlyApproved && !approvedChannels.includes(claim.dataValues.certificateId)) { + if (serveOnlyApproved && !isApprovedChannel({ longId: claim.dataValues.certificateId })) { throw new Error(UNAPPROVED_CHANNEL); } logger.debug('Outpoint:', claim.dataValues.outpoint); diff --git a/server/models/claim.js b/server/models/claim.js index 55010987..6917d189 100644 --- a/server/models/claim.js +++ b/server/models/claim.js @@ -1,7 +1,8 @@ const logger = require('winston'); const returnShortId = require('./utils/returnShortId.js'); const { assetDefaults: { thumbnail: defaultThumbnail }, details: { host } } = require('@config/siteConfig'); -const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig'); +const { publishing: { serveOnlyApproved } } = require('@config/siteConfig'); +const isApprovedChannel = require('../utils/isApprovedChannel'); const NO_CLAIM = 'NO_CLAIM'; const NOT_ALLOWED = 'NOT_ALLOWED'; @@ -364,7 +365,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { where: { name, claimId }, }) .then(claimArray => { - if (serveOnlyApproved && !approvedChannels.includes(claimArray[0].dataValues.certificateId)) { + if (serveOnlyApproved && !isApprovedChannel({ longId: claimArray[0].dataValues.certificateId })) { reject(NOT_ALLOWED); } switch (claimArray.length) { diff --git a/server/utils/isApprovedChannel.js b/server/utils/isApprovedChannel.js new file mode 100644 index 00000000..e1b451e2 --- /dev/null +++ b/server/utils/isApprovedChannel.js @@ -0,0 +1,11 @@ +const { publishing: { approvedChannels } } = require('@config/siteConfig'); + +function isApprovedChannel (channel, channels = approvedChannels) { + const { name, shortId: short, longId: long } = channel; + return Boolean( + (long && channels.find(chan => chan.longId === long)) || + (name && short && channels.find(chan => chan.name === name && chan.shortId === short)) + ); +} + +module.exports = isApprovedChannel; From 208c56366fbd17fef6758807d03806823c57ac55 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Thu, 20 Sep 2018 14:09:01 -0400 Subject: [PATCH 06/15] refactor error messages for unavailable content --- server/controllers/api/channel/data/index.js | 2 +- .../controllers/assets/utils/getClaimIdAndServeAsset.js | 8 ++++---- server/models/claim.js | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/server/controllers/api/channel/data/index.js b/server/controllers/api/channel/data/index.js index 440326b5..b9513edc 100644 --- a/server/controllers/api/channel/data/index.js +++ b/server/controllers/api/channel/data/index.js @@ -21,7 +21,7 @@ const channelData = ({ ip, originalUrl, body, params }, res) => { if (serveOnlyApproved && !isApprovedChannel(chanObj)) { return res.status(404).json({ success: false, - message: 'This spee.ch instance serves limited content which does not include this asset', + message: 'This content is unavailable', }); } diff --git a/server/controllers/assets/utils/getClaimIdAndServeAsset.js b/server/controllers/assets/utils/getClaimIdAndServeAsset.js index af81162c..031bfc67 100644 --- a/server/controllers/assets/utils/getClaimIdAndServeAsset.js +++ b/server/controllers/assets/utils/getClaimIdAndServeAsset.js @@ -12,7 +12,7 @@ const NO_CHANNEL = 'NO_CHANNEL'; const NO_CLAIM = 'NO_CLAIM'; const BLOCKED_CLAIM = 'BLOCKED_CLAIM'; const NO_FILE = 'NO_FILE'; -const UNAPPROVED_CHANNEL = 'UNAPPROVED_CHANNEL'; +const CONTENT_UNAVAILABLE = 'CONTENT_UNAVAILABLE'; const { publishing: { serveOnlyApproved } } = require('@config/siteConfig'); @@ -30,7 +30,7 @@ const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId }) .then(claim => { if (serveOnlyApproved && !isApprovedChannel({ longId: claim.dataValues.certificateId })) { - throw new Error(UNAPPROVED_CHANNEL); + throw new Error(CONTENT_UNAVAILABLE); } logger.debug('Outpoint:', claim.dataValues.outpoint); return db.Blocked.isNotBlocked(claim.dataValues.outpoint); @@ -64,11 +64,11 @@ const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId message: 'No matching channel id could be found for that url', }); } - if (error === UNAPPROVED_CHANNEL) { + if (error === CONTENT_UNAVAILABLE) { logger.debug('unapproved channel'); return res.status(400).json({ success: false, - message: 'This spee.ch instance serves limited content which does not include this asset', + message: 'This content is unavailable', }); } if (error === BLOCKED_CLAIM) { diff --git a/server/models/claim.js b/server/models/claim.js index 6917d189..8987b7a1 100644 --- a/server/models/claim.js +++ b/server/models/claim.js @@ -1,11 +1,10 @@ const logger = require('winston'); const returnShortId = require('./utils/returnShortId.js'); +const isApprovedChannel = require('../utils/isApprovedChannel'); const { assetDefaults: { thumbnail: defaultThumbnail }, details: { host } } = require('@config/siteConfig'); const { publishing: { serveOnlyApproved } } = require('@config/siteConfig'); -const isApprovedChannel = require('../utils/isApprovedChannel'); const NO_CLAIM = 'NO_CLAIM'; -const NOT_ALLOWED = 'NOT_ALLOWED'; function determineFileExtensionFromContentType (contentType) { switch (contentType) { @@ -366,7 +365,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { }) .then(claimArray => { if (serveOnlyApproved && !isApprovedChannel({ longId: claimArray[0].dataValues.certificateId })) { - reject(NOT_ALLOWED); + reject('This content is unavailable'); } switch (claimArray.length) { case 0: From 31891a553ef65a0a10ec2d7b63f9f64b480729bf Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Fri, 21 Sep 2018 11:38:06 -0400 Subject: [PATCH 07/15] load home channel content on homepage when publishing is locked down and user is not logged in --- client/src/pages/ContentPageWrapper/view.jsx | 6 ++++-- client/src/pages/HomePage/index.jsx | 3 ++- client/src/pages/HomePage/view.jsx | 7 +++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/client/src/pages/ContentPageWrapper/view.jsx b/client/src/pages/ContentPageWrapper/view.jsx index e88f8001..9bb8e8cf 100644 --- a/client/src/pages/ContentPageWrapper/view.jsx +++ b/client/src/pages/ContentPageWrapper/view.jsx @@ -3,12 +3,14 @@ import ErrorPage from '@pages/ErrorPage'; import ShowAssetLite from '@pages/ShowAssetLite'; import ShowAssetDetails from '@pages/ShowAssetDetails'; import ShowChannel from '@pages/ShowChannel'; +import { withRouter } from 'react-router-dom'; import { CHANNEL, ASSET_LITE, ASSET_DETAILS } from '../../constants/show_request_types'; class ContentPageWrapper extends React.Component { componentDidMount () { - this.props.onHandleShowPageUri(this.props.match.params); + const { onHandleShowPageUri, match, homeChannel } = this.props; + onHandleShowPageUri(homeChannel ? { claim: homeChannel } : match.params); } componentWillReceiveProps (nextProps) { if (nextProps.match.params !== this.props.match.params) { @@ -35,4 +37,4 @@ class ContentPageWrapper extends React.Component { } }; -export default ContentPageWrapper; +export default withRouter(ContentPageWrapper); diff --git a/client/src/pages/HomePage/index.jsx b/client/src/pages/HomePage/index.jsx index f8357964..367e2675 100644 --- a/client/src/pages/HomePage/index.jsx +++ b/client/src/pages/HomePage/index.jsx @@ -2,10 +2,11 @@ import { connect } from 'react-redux'; import { onHandleShowPageUri } from '../../actions/show'; import View from './view'; -const mapStateToProps = ({ show }) => { +const mapStateToProps = ({ show, site, channel }) => { return { error : show.request.error, requestType: show.request.type, + homeChannel: site.publishOnlyApproved && !channel.loggedInChannel.name ? `${site.approvedChannels[0].name}:${site.approvedChannels[0].longId}` : null, }; }; diff --git a/client/src/pages/HomePage/view.jsx b/client/src/pages/HomePage/view.jsx index 8ca1e065..05adfe45 100644 --- a/client/src/pages/HomePage/view.jsx +++ b/client/src/pages/HomePage/view.jsx @@ -1,11 +1,14 @@ import React from 'react'; import PageLayout from '@components/PageLayout'; - import PublishTool from '@containers/PublishTool'; +import ContentPageWrapper from '@pages/ContentPageWrapper'; class HomePage extends React.Component { render () { - return ( + const { homeChannel } = this.props; + return homeChannel ? ( + + ) : ( Date: Fri, 21 Sep 2018 12:00:10 -0400 Subject: [PATCH 08/15] add multisite default config options --- cli/defaults/siteConfig.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cli/defaults/siteConfig.json b/cli/defaults/siteConfig.json index 8359f0e6..91efe08f 100644 --- a/cli/defaults/siteConfig.json +++ b/cli/defaults/siteConfig.json @@ -26,7 +26,11 @@ "thumbnailChannelId": null, "additionalClaimAddresses": [], "disabled": false, - "disabledMessage": "Default publishing disabled message" + "disabledMessage": "Default publishing disabled message", + "closedRegistration": false, + "serveOnlyApproved": false, + "publishOnlyApproved": false, + "approvedChannels": [] }, "startup": { "performChecks": true, From 69f82d814b7eac5a1dd63e682c34d52d34ac9240 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Fri, 21 Sep 2018 16:16:01 -0400 Subject: [PATCH 09/15] refactor isApprovedClaim to claims folder shared by client and server --- client/src/containers/NavigationLinks/view.jsx | 2 +- server/controllers/api/channel/data/index.js | 6 +++--- server/controllers/api/claim/publish/index.js | 6 +++--- .../assets/utils/getClaimIdAndServeAsset.js | 6 +++--- server/models/claim.js | 6 +++--- server/utils/isApprovedChannel.js | 11 ----------- {client/src/utils => utils}/isApprovedChannel.js | 0 7 files changed, 13 insertions(+), 24 deletions(-) delete mode 100644 server/utils/isApprovedChannel.js rename {client/src/utils => utils}/isApprovedChannel.js (100%) diff --git a/client/src/containers/NavigationLinks/view.jsx b/client/src/containers/NavigationLinks/view.jsx index 2f8ce8ca..c1a71335 100644 --- a/client/src/containers/NavigationLinks/view.jsx +++ b/client/src/containers/NavigationLinks/view.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { NavLink, withRouter } from 'react-router-dom'; import NavBarChannelOptionsDropdown from '@components/NavBarChannelOptionsDropdown'; -import isApprovedChannel from '../../utils/isApprovedChannel'; +import isApprovedChannel from '../../../../utils/isApprovedChannel'; const VIEW = 'VIEW'; const LOGOUT = 'LOGOUT'; diff --git a/server/controllers/api/channel/data/index.js b/server/controllers/api/channel/data/index.js index b9513edc..c0401e1f 100644 --- a/server/controllers/api/channel/data/index.js +++ b/server/controllers/api/channel/data/index.js @@ -1,7 +1,7 @@ const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); const getChannelData = require('./getChannelData.js'); -const isApprovedChannel = require('../../../../utils/isApprovedChannel'); -const { publishing: { serveOnlyApproved } } = require('@config/siteConfig'); +const isApprovedChannel = require('../../../../../utils/isApprovedChannel'); +const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig'); const NO_CHANNEL = 'NO_CHANNEL'; /* @@ -18,7 +18,7 @@ const channelData = ({ ip, originalUrl, body, params }, res) => { const chanObj = {}; if (channelName) chanObj.name = channelName; if (channelClaimId) chanObj[(channelClaimId.length === 40 ? 'longId' : 'shortId')] = channelClaimId; - if (serveOnlyApproved && !isApprovedChannel(chanObj)) { + if (serveOnlyApproved && !isApprovedChannel(chanObj, approvedChannels)) { return res.status(404).json({ success: false, message: 'This content is unavailable', diff --git a/server/controllers/api/claim/publish/index.js b/server/controllers/api/claim/publish/index.js index c5a2bdbb..7fa525d0 100644 --- a/server/controllers/api/claim/publish/index.js +++ b/server/controllers/api/claim/publish/index.js @@ -3,8 +3,8 @@ const logger = require('winston'); const { details: { host }, publishing: { disabled, disabledMessage } } = require('@config/siteConfig'); const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js'); -const isApprovedChannel = require('../../../../utils/isApprovedChannel'); -const { publishing: { publishOnlyApproved } } = require('@config/siteConfig'); +const isApprovedChannel = require('../../../../../utils/isApprovedChannel'); +const { publishing: { publishOnlyApproved, approvedChannels } } = require('@config/siteConfig'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); @@ -57,7 +57,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) // check channel authorization authenticateUser(channelName, channelId, channelPassword, user) .then(({ channelName, channelClaimId }) => { - if (publishOnlyApproved && !isApprovedChannel({ longId: channelClaimId })) { + if (publishOnlyApproved && !isApprovedChannel({ longId: channelClaimId }, approvedChannels)) { const error = { name : UNAPPROVED_CHANNEL, message: 'This spee.ch instance only allows publishing to approved channels', diff --git a/server/controllers/assets/utils/getClaimIdAndServeAsset.js b/server/controllers/assets/utils/getClaimIdAndServeAsset.js index 031bfc67..1feb5ba2 100644 --- a/server/controllers/assets/utils/getClaimIdAndServeAsset.js +++ b/server/controllers/assets/utils/getClaimIdAndServeAsset.js @@ -1,7 +1,7 @@ const logger = require('winston'); const db = require('../../../models'); -const isApprovedChannel = require('../../../utils/isApprovedChannel'); +const isApprovedChannel = require('../../../../utils/isApprovedChannel'); const getClaimId = require('../../utils/getClaimId.js'); const { handleErrorResponse } = require('../../utils/errorHandlers.js'); @@ -14,7 +14,7 @@ const BLOCKED_CLAIM = 'BLOCKED_CLAIM'; const NO_FILE = 'NO_FILE'; const CONTENT_UNAVAILABLE = 'CONTENT_UNAVAILABLE'; -const { publishing: { serveOnlyApproved } } = require('@config/siteConfig'); +const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig'); const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId, originalUrl, ip, res) => { getClaimId(channelName, channelClaimId, claimName, claimId) @@ -29,7 +29,7 @@ const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId }); }) .then(claim => { - if (serveOnlyApproved && !isApprovedChannel({ longId: claim.dataValues.certificateId })) { + if (serveOnlyApproved && !isApprovedChannel({ longId: claim.dataValues.certificateId }, approvedChannels)) { throw new Error(CONTENT_UNAVAILABLE); } logger.debug('Outpoint:', claim.dataValues.outpoint); diff --git a/server/models/claim.js b/server/models/claim.js index 8987b7a1..0385cc02 100644 --- a/server/models/claim.js +++ b/server/models/claim.js @@ -1,8 +1,8 @@ const logger = require('winston'); const returnShortId = require('./utils/returnShortId.js'); -const isApprovedChannel = require('../utils/isApprovedChannel'); +const isApprovedChannel = require('../../utils/isApprovedChannel'); const { assetDefaults: { thumbnail: defaultThumbnail }, details: { host } } = require('@config/siteConfig'); -const { publishing: { serveOnlyApproved } } = require('@config/siteConfig'); +const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig'); const NO_CLAIM = 'NO_CLAIM'; @@ -364,7 +364,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { where: { name, claimId }, }) .then(claimArray => { - if (serveOnlyApproved && !isApprovedChannel({ longId: claimArray[0].dataValues.certificateId })) { + if (serveOnlyApproved && !isApprovedChannel({ longId: claimArray[0].dataValues.certificateId }, approvedChannels)) { reject('This content is unavailable'); } switch (claimArray.length) { diff --git a/server/utils/isApprovedChannel.js b/server/utils/isApprovedChannel.js deleted file mode 100644 index e1b451e2..00000000 --- a/server/utils/isApprovedChannel.js +++ /dev/null @@ -1,11 +0,0 @@ -const { publishing: { approvedChannels } } = require('@config/siteConfig'); - -function isApprovedChannel (channel, channels = approvedChannels) { - const { name, shortId: short, longId: long } = channel; - return Boolean( - (long && channels.find(chan => chan.longId === long)) || - (name && short && channels.find(chan => chan.name === name && chan.shortId === short)) - ); -} - -module.exports = isApprovedChannel; diff --git a/client/src/utils/isApprovedChannel.js b/utils/isApprovedChannel.js similarity index 100% rename from client/src/utils/isApprovedChannel.js rename to utils/isApprovedChannel.js From 6d6bb40fe65a756f841f663e36ed62a3fb0d30eb Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 24 Sep 2018 09:22:28 -0400 Subject: [PATCH 10/15] move approved channel check to its own method --- server/models/claim.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/server/models/claim.js b/server/models/claim.js index 0385cc02..63aa8b94 100644 --- a/server/models/claim.js +++ b/server/models/claim.js @@ -356,7 +356,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { } }; - Claim.resolveClaim = function (name, claimId) { + Claim.fetchClaim = function (name, claimId) { logger.debug(`Claim.resolveClaim: ${name} ${claimId}`); return new Promise((resolve, reject) => { this @@ -364,9 +364,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { where: { name, claimId }, }) .then(claimArray => { - if (serveOnlyApproved && !isApprovedChannel({ longId: claimArray[0].dataValues.certificateId }, approvedChannels)) { - reject('This content is unavailable'); - } switch (claimArray.length) { case 0: return resolve(null); @@ -383,6 +380,23 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { }); }; + Claim.resolveClaim = function (name, claimId) { + return new Promise((resolve, reject) => { + this + .fetchClaim(name, claimId) + .then(claim => { + logger.info('resolveClaim claims:', claim); + if (serveOnlyApproved && !isApprovedChannel({ longId: claim.certificateId }, approvedChannels)) { + throw new Error('This content is unavailable'); + } + return resolve(claim); + }) + .catch(error => { + reject(error); + }); + }); + }; + Claim.getOutpoint = function (name, claimId) { logger.debug(`finding outpoint for ${name}#${claimId}`); return this From 7fd0e66d9b223294be7a587f4b503f745e4e27b0 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 24 Sep 2018 09:25:32 -0400 Subject: [PATCH 11/15] refactor nav links display; move logic to index.js --- client/src/containers/NavigationLinks/index.jsx | 10 ++++++---- client/src/containers/NavigationLinks/view.jsx | 7 +++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/client/src/containers/NavigationLinks/index.jsx b/client/src/containers/NavigationLinks/index.jsx index 7ef8ff85..d5a9c454 100644 --- a/client/src/containers/NavigationLinks/index.jsx +++ b/client/src/containers/NavigationLinks/index.jsx @@ -1,13 +1,15 @@ import { connect } from 'react-redux'; import { logOutChannel, checkForLoggedInChannel } from '../../actions/channel'; +import isApprovedChannel from '../../../../utils/isApprovedChannel'; import View from './view'; const mapStateToProps = ({ site, channel: { loggedInChannel: { name, shortId, longId } } }) => { return { - site, - channelName : name, - channelShortId: shortId, - channelLongId : longId, + showPublish : (!site.publishOnlyApproved || isApprovedChannel({ longId }, site.approvedChannels)), + closedRegistration: site.closedRegistration, + channelName : name, + channelShortId : shortId, + channelLongId : longId, }; }; diff --git a/client/src/containers/NavigationLinks/view.jsx b/client/src/containers/NavigationLinks/view.jsx index c1a71335..01775688 100644 --- a/client/src/containers/NavigationLinks/view.jsx +++ b/client/src/containers/NavigationLinks/view.jsx @@ -1,7 +1,6 @@ import React from 'react'; import { NavLink, withRouter } from 'react-router-dom'; import NavBarChannelOptionsDropdown from '@components/NavBarChannelOptionsDropdown'; -import isApprovedChannel from '../../../../utils/isApprovedChannel'; const VIEW = 'VIEW'; const LOGOUT = 'LOGOUT'; @@ -29,10 +28,10 @@ class NavigationLinks extends React.Component { } } render () { - const { site, channelLongId, channelName } = this.props; + const { channelName, showPublish, closedRegistration } = this.props; return (
- {(!site.publishOnlyApproved || isApprovedChannel({ longId: channelLongId }, site.approvedChannels)) && - ) : !site.closedRegistration && ( + ) : !closedRegistration && ( Date: Mon, 24 Sep 2018 09:26:16 -0400 Subject: [PATCH 12/15] handle exceptions together --- server/controllers/api/claim/publish/index.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/server/controllers/api/claim/publish/index.js b/server/controllers/api/claim/publish/index.js index 7fa525d0..f51d4055 100644 --- a/server/controllers/api/claim/publish/index.js +++ b/server/controllers/api/claim/publish/index.js @@ -102,13 +102,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) sendGATimingEvent('end-to-end', 'publish', fileType, gaStartTime, Date.now()); }) .catch(error => { - if (error.name === CLAIM_TAKEN) { - res.status(400).json({ - success: false, - message: error.message, - }); - } - if (error.name === UNAPPROVED_CHANNEL) { + if ([CLAIM_TAKEN, UNAPPROVED_CHANNEL].includes(error.name)) { res.status(400).json({ success: false, message: error.message, From 50cbe126498bec850506e55be1db976e313b6cf9 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 24 Sep 2018 09:26:58 -0400 Subject: [PATCH 13/15] use constants to refer to claim length, longId, shortId --- server/controllers/api/channel/data/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/controllers/api/channel/data/index.js b/server/controllers/api/channel/data/index.js index c0401e1f..612eb016 100644 --- a/server/controllers/api/channel/data/index.js +++ b/server/controllers/api/channel/data/index.js @@ -2,7 +2,11 @@ const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); const getChannelData = require('./getChannelData.js'); const isApprovedChannel = require('../../../../../utils/isApprovedChannel'); const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig'); + const NO_CHANNEL = 'NO_CHANNEL'; +const LONG_ID = 'longId'; +const SHORT_ID = 'shortId'; +const LONG_CLAIM_LENGTH = 40; /* @@ -14,10 +18,9 @@ const channelData = ({ ip, originalUrl, body, params }, res) => { const channelName = params.channelName; let channelClaimId = params.channelClaimId; if (channelClaimId === 'none') channelClaimId = null; - const chanObj = {}; if (channelName) chanObj.name = channelName; - if (channelClaimId) chanObj[(channelClaimId.length === 40 ? 'longId' : 'shortId')] = channelClaimId; + if (channelClaimId) chanObj[(channelClaimId.length === LONG_CLAIM_LENGTH ? LONG_ID : SHORT_ID)] = channelClaimId; if (serveOnlyApproved && !isApprovedChannel(chanObj, approvedChannels)) { return res.status(404).json({ success: false, From fda83dca5b8d03bd6b15a013e9669923b7317934 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Thu, 4 Oct 2018 15:25:53 -0400 Subject: [PATCH 14/15] force publishes to loggedInChannel when publishOnlyApproved --- client/src/containers/ChannelSelect/index.js | 9 +++++++-- client/src/containers/ChannelSelect/view.jsx | 19 ++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/client/src/containers/ChannelSelect/index.js b/client/src/containers/ChannelSelect/index.js index 9efce26d..23d731ad 100644 --- a/client/src/containers/ChannelSelect/index.js +++ b/client/src/containers/ChannelSelect/index.js @@ -1,13 +1,18 @@ import {connect} from 'react-redux'; import {setPublishInChannel, updateSelectedChannel, updateError} from '../../actions/publish'; +// import isApprovedChannel from '../../../../utils/isApprovedChannel'; import View from './view'; -const mapStateToProps = ({ channel, publish }) => { +const mapStateToProps = ({ publish, site, channel: { loggedInChannel: { name, shortId, longId } } }) => { return { - loggedInChannelName: channel.loggedInChannel.name, + // isApprovedChannel : isApprovedChannel({ longId }, site.approvedChannels), + publishOnlyApproved: site.publishOnlyApproved, + // closedRegistration : site.closedRegistration, + loggedInChannelName: name, publishInChannel : publish.publishInChannel, selectedChannel : publish.selectedChannel, channelError : publish.error.channel, + longId, }; }; diff --git a/client/src/containers/ChannelSelect/view.jsx b/client/src/containers/ChannelSelect/view.jsx index 4a4dc5fe..d04007d5 100644 --- a/client/src/containers/ChannelSelect/view.jsx +++ b/client/src/containers/ChannelSelect/view.jsx @@ -16,9 +16,12 @@ class ChannelSelect extends React.Component { this.handleSelection = this.handleSelection.bind(this); } componentWillMount () { - const { loggedInChannelName } = this.props; + const { loggedInChannelName, onChannelSelect, publishOnlyApproved, onPublishInChannelChange } = this.props; if (loggedInChannelName) { - this.props.onChannelSelect(loggedInChannelName); + onChannelSelect(loggedInChannelName); + } + if (publishOnlyApproved) { + onPublishInChannelChange(true); } } toggleAnonymousPublish (event) { @@ -34,7 +37,17 @@ class ChannelSelect extends React.Component { this.props.onChannelSelect(selectedOption); } render () { - const { publishInChannel, channelError, selectedChannel, loggedInChannelName } = this.props; + const { publishInChannel, channelError, selectedChannel, loggedInChannelName, publishOnlyApproved } = this.props; + if (publishOnlyApproved) { + return ( +
+ } + content={{loggedInChannelName}} + /> +
+ ); + } return (
Date: Thu, 4 Oct 2018 15:48:49 -0400 Subject: [PATCH 15/15] add quotes to bid amount --- cli/configure.js | 2 +- server/controllers/api/claim/publish/createPublishParams.js | 2 +- .../api/claim/publish/createThumbnailPublishParams.js | 2 +- server/lbrynet/index.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/configure.js b/cli/configure.js index 0d59cbbd..c50b81fa 100644 --- a/cli/configure.js +++ b/cli/configure.js @@ -157,7 +157,7 @@ inquirer method: 'channel_new', params: { channel_name: thumbnailChannelDefault, - amount : 0.1, + amount : '0.1', }, }) .then(response => { diff --git a/server/controllers/api/claim/publish/createPublishParams.js b/server/controllers/api/claim/publish/createPublishParams.js index facdc32f..a278bbee 100644 --- a/server/controllers/api/claim/publish/createPublishParams.js +++ b/server/controllers/api/claim/publish/createPublishParams.js @@ -18,7 +18,7 @@ const createPublishParams = (filePath, name, title, description, license, nsfw, const publishParams = { name, file_path: filePath, - bid : 0.01, + bid : '0.01', metadata : { description, title, diff --git a/server/controllers/api/claim/publish/createThumbnailPublishParams.js b/server/controllers/api/claim/publish/createThumbnailPublishParams.js index 1032c9a3..668bd07c 100644 --- a/server/controllers/api/claim/publish/createThumbnailPublishParams.js +++ b/server/controllers/api/claim/publish/createThumbnailPublishParams.js @@ -10,7 +10,7 @@ const createThumbnailPublishParams = (thumbnailFilePath, claimName, license, nsf return { name : `${claimName}-thumb`, file_path: thumbnailFilePath, - bid : 0.01, + bid : '0.01', metadata : { title : `${claimName} thumbnail`, description: `a thumbnail for ${claimName}`, diff --git a/server/lbrynet/index.js b/server/lbrynet/index.js index 4ff64521..655909fa 100644 --- a/server/lbrynet/index.js +++ b/server/lbrynet/index.js @@ -116,7 +116,7 @@ module.exports = { method: 'channel_new', params: { channel_name: name, - amount : 0.1, + amount : '0.1', }, }) .then(response => {