Browse Source

Merge pull request #2513 from weseek/fix/create-middlre-ware-certify-shared-page

Fix/create middlre ware certify shared page
Yuki Takei 5 years ago
parent
commit
cc8e417a20

+ 1 - 1
src/client/js/app.jsx

@@ -141,7 +141,7 @@ $('a[data-toggle="tab"][href="#revision-history"]').on('show.bs.tab', () => {
   ReactDOM.render(
     <I18nextProvider i18n={i18n}>
       <ErrorBoundary>
-        <PageHistory pageId={pageContainer.state.pageId} crowi={appContainer} />
+        <PageHistory shareLinkId={pageContainer.state.shareLinkId} pageId={pageContainer.state.pageId} crowi={appContainer} />
       </ErrorBoundary>
     </I18nextProvider>, document.getElementById('revision-history'),
   );

+ 7 - 2
src/client/js/components/PageHistory.jsx

@@ -26,6 +26,7 @@ class PageHistory extends React.Component {
 
   async componentWillMount() {
     const pageId = this.props.pageId;
+    const shareLinkId = this.props.shareLinkId || null;
 
     if (!pageId) {
       return;
@@ -34,7 +35,7 @@ class PageHistory extends React.Component {
     let res;
     try {
       this.setState({ isLoading: true });
-      res = await this.props.crowi.apiGet('/revisions.ids', { page_id: pageId });
+      res = await this.props.crowi.apiGet('/revisions.ids', { page_id: pageId, share_link_id: shareLinkId });
     }
     catch (err) {
       logger.error(err);
@@ -110,12 +111,14 @@ class PageHistory extends React.Component {
   }
 
   fetchPageRevisionBody(revision) {
+    const shareLinkId = this.props.shareLinkId || null;
+
     if (revision.body) {
       return;
     }
 
     this.props.crowi.apiGet('/revisions.get',
-      { page_id: this.props.pageId, revision_id: revision._id })
+      { page_id: this.props.pageId, revision_id: revision._id, share_link_id: shareLinkId })
       .then((res) => {
         if (res.ok) {
           this.setState({
@@ -166,6 +169,8 @@ class PageHistory extends React.Component {
 
 PageHistory.propTypes = {
   t: PropTypes.func.isRequired, // i18next
+
+  shareLinkId: PropTypes.string,
   pageId: PropTypes.string,
   crowi: PropTypes.object.isRequired,
 };

+ 1 - 0
src/client/js/services/PageContainer.js

@@ -65,6 +65,7 @@ export default class PageContainer extends Container {
       hasChildren: JSON.parse(mainContent.getAttribute('data-page-has-children')),
       templateTagData: mainContent.getAttribute('data-template-tags') || null,
       shareLinksNumber:  mainContent.getAttribute('data-share-links-number'),
+      shareLinkId: JSON.parse(mainContent.getAttribute('data-share-link-id') || null),
 
       // latest(on remote) information
       remoteRevisionId: revisionId,

+ 31 - 0
src/server/middlewares/certify-shared-page.js

@@ -0,0 +1,31 @@
+const loggerFactory = require('@alias/logger');
+
+const logger = loggerFactory('growi:middleware:certify-shared-page');
+
+module.exports = (crowi) => {
+
+  return async(req, res, next) => {
+    const pageId = req.query.page_id || req.body.page_id || null;
+    const shareLinkId = req.query.share_link_id || req.body.share_link_id || null;
+    if (pageId == null || shareLinkId == null) {
+      return next();
+    }
+
+    const ShareLink = crowi.model('ShareLink');
+    const sharelink = await ShareLink.findOne({ _id: shareLinkId, relatedPage: pageId });
+
+    // check sharelink enabled
+    if (sharelink == null || sharelink.isExpired()) {
+      return next();
+    }
+
+    logger.debug('shareLink id is', sharelink._id);
+
+    req.isSharedPage = true;
+
+    logger.debug('Confirmed target page id is a share page');
+
+    next();
+  };
+
+};

+ 6 - 0
src/server/middlewares/login-required.js

@@ -17,6 +17,12 @@ module.exports = (crowi, isGuestAllowed = false) => {
       return next();
     }
 
+    // check the page is shared
+    if (isGuestAllowed && req.isSharedPage) {
+      logger.debug('Target page is shared page');
+      return next();
+    }
+
     const User = crowi.model('User');
 
     // check the user logged in

+ 3 - 2
src/server/routes/index.js

@@ -10,6 +10,7 @@ module.exports = function(crowi, app) {
   const loginRequiredStrictly = require('../middlewares/login-required')(crowi);
   const loginRequired = require('../middlewares/login-required')(crowi, true);
   const adminRequired = require('../middlewares/admin-required')(crowi);
+  const certifySharedPage = require('../middlewares/certify-shared-page')(crowi);
   const csrf = require('../middlewares/csrf')(crowi);
 
   const uploads = multer({ dest: `${crowi.tmpDir}uploads` });
@@ -164,8 +165,8 @@ module.exports = function(crowi, app) {
   app.post('/_api/attachments.removeProfileImage'   , accessTokenParser , loginRequiredStrictly , csrf, attachment.api.removeProfileImage);
   app.get('/_api/attachments.limit'   , accessTokenParser , loginRequiredStrictly, attachment.api.limit);
 
-  app.get('/_api/revisions.get'       , accessTokenParser , loginRequired , revision.api.get);
-  app.get('/_api/revisions.ids'       , accessTokenParser , loginRequired , revision.api.ids);
+  app.get('/_api/revisions.get'       , certifySharedPage , accessTokenParser , loginRequired , revision.api.get);
+  app.get('/_api/revisions.ids'       , certifySharedPage , accessTokenParser , loginRequired , revision.api.ids);
   app.get('/_api/revisions.list'      , accessTokenParser , loginRequired , revision.api.list);
 
   app.get('/trash$'                   , loginRequired , page.trashPageShowWrapper);

+ 2 - 0
src/server/routes/page.js

@@ -480,6 +480,8 @@ module.exports = function(crowi, app) {
       return res.render('page_presentation', renderVars);
     }
 
+    page.initLatestRevisionField(revisionId);
+
     // populate
     page = await page.populateDataToShowRevision();
     addRendarVarsForPage(renderVars, page);

+ 6 - 1
src/server/views/layout-growi/shared_page.html

@@ -14,7 +14,12 @@
 {% endblock %}
 
 {% block content_main %}
-  <div class="row" id="is-shared-page" data-share-link-expired-at="{% if sharelink.expiredAt %}{{ sharelink.expiredAt|datetz('Y/m/d H:i:s')}}{% endif %}" data-share-link-created-at="{{ sharelink.createdAt|datetz('Y/m/d H:i:s')}}">
+  <div
+    class="row"
+    id="is-shared-page"
+    data-share-link-expired-at="{% if sharelink.expiredAt %}{{ sharelink.expiredAt|datetz('Y/m/d H:i:s')}}{% endif %}"
+    data-share-link-created-at="{{ sharelink.createdAt|datetz('Y/m/d H:i:s')}}"
+  >
     {% block content_page %}
       <div class="col grw-page-content-container">
         <div id="share-link-alert"></div>

+ 1 - 0
src/server/views/widget/page_content.html

@@ -24,6 +24,7 @@
   data-page-updated-at="{% if page %}{{ page.updatedAt|datetz('Y/m/d H:i:s') }}{% endif %}"
   data-page-has-children="{% if pages.length > 0 %}true{% else %}false{% endif %}"
   data-share-links-number="{% if page %}{{ sharelinksNumber }}{% endif %}"
+  data-share-link-id="{% if sharelink %}{{ sharelink._id|json }}{% endif %}"
   data-page-user="{% if pageUser %}{{ pageUser|json }}{% else %}null{% endif %}"
   >
 {% else %}

+ 16 - 0
src/test/middlewares/login-required.test.js

@@ -52,6 +52,22 @@ describe('loginRequired', () => {
       expect(result).toBe('redirect');
     });
 
+    test('pass anyone into sharedPage when aclService.isGuestAllowedToRead() returns false', () => {
+
+      req.isSharedPage = true;
+
+      // prepare spy for AclService.isGuestAllowedToRead
+      const isGuestAllowedToReadSpy = jest.spyOn(crowi.aclService, 'isGuestAllowedToRead')
+        .mockImplementation(() => false);
+
+      const result = loginRequired(req, res, next);
+
+      expect(isGuestAllowedToReadSpy).toHaveBeenCalled();
+      expect(next).toHaveBeenCalled();
+      expect(res.redirect).not.toHaveBeenCalled();
+      expect(result).toBe('next');
+    });
+
   });
 
 

+ 3 - 0
src/test/models/shareLink.test.js

@@ -42,6 +42,9 @@ describe('ShareLink', () => {
           creator: {},
         };
       },
+      initLatestRevisionField: (revisionId) => {
+        return revisionId;
+      },
     };
 
     test('share link is not found', async() => {