Browse Source

Add reCAPTCHA functionality

Optional recaptcha key variables can be set to enable reCAPTCHA
Ryno Mathee 6 years ago
parent
commit
d3d7659a1c
5 changed files with 75 additions and 31 deletions
  1. 4 0
      README.md
  2. 4 1
      config.js
  3. 4 0
      public/css/style.css
  4. 59 30
      routes/index.js
  5. 4 0
      views/index.jade

+ 4 - 0
README.md

@@ -24,6 +24,8 @@ Fill out `config.js` as your infomation.
 * `slackUrl` : your slack team url (ex.: socketio.slack.com)
 * `slacktoken` : Your access token for Slack. (see [Issue token](#issue-token))
 * `inviteToken`: An optional security measure - if it is set, then that token will be required to get invited.
+* `recaptchaSiteKey`: An optional security measure - if it is set, and `recaptchaSecretKey` is set, then a captcha will be required to get invited.
+* `recaptchaSecretKey`: An optional security measure - if it is set, and `recaptchaSiteKey` is set, then a captcha will be required to get invited.
 * `locale`: Application language (currently `cs`, `de`, `en`, `es`, `fr`, `it`,  `ja`, `ko`, `pl`, `pt`, `pt-BR`, `tr`, `zh-CN` and `zh-TW` available).
 
 ### Environment Variables
@@ -35,6 +37,8 @@ If you want to use a `.env` file, create a file in the root called `.env` with t
 - `SLACK_URL` : Your Slack team url (ex.: socketio.slack.com)
 - `SLACK_TOKEN` : Your access token for Slack. (see [Issue token](#issue-token))
 - `INVITE_TOKEN`: An optional security measure - if it is set, then that token will be required to get invited.
+- `RECAPTCHA_SITE`: An optional security measure - used to enable reCAPTCHA.
+- `RECAPTCHA_SECRET`: An optional security measure - used to enable reCAPTCHA.
 - `LOCALE`: Application language (currently `cs`, `de`, `en`, `es`, `fr`, `it`, `ja`, `ko`, `pl`, `pt`, `pt-BR`, `tr`, `zh-CN` and `zh-TW` available).
 
 **Sample**

+ 4 - 1
config.js

@@ -15,6 +15,9 @@ module.exports = {
   slacktoken: process.env.SLACK_TOKEN || 'YOUR-ACCESS-TOKEN',
   // an optional security measure - if it is set, then that token will be required to get invited.
   inviteToken: process.env.INVITE_TOKEN || null,
-
+  // an optional security measure - if both are set, then recaptcha will be used.
+  recaptchaSiteKey: process.env.RECAPTCHA_SITE || null,
+  recaptchaSecretKey: process.env.RECAPTCHA_SECRET || null,
+  // default locale
   locale: process.env.LOCALE || "en",
 };

+ 4 - 0
public/css/style.css

@@ -158,3 +158,7 @@ a:visited,
 a:hover {
   color: #7EB6FF;
 }
+
+.g-recaptcha, .g-recaptcha > div {
+  margin: auto;
+}

+ 59 - 30
routes/index.js

@@ -6,45 +6,71 @@ var config = require('../config');
 router.get('/', function(req, res) {
   res.setLocale(config.locale);
   res.render('index', { community: config.community,
-                        tokenRequired: !!config.inviteToken });
+                        tokenRequired: !!config.inviteToken,
+                        recaptchaSiteKey: config.recaptchaSiteKey });
 });
 
 router.post('/invite', function(req, res) {
   if (req.body.email && (!config.inviteToken || (!!config.inviteToken && req.body.token === config.inviteToken))) {
-    request.post({
-        url: 'https://'+ config.slackUrl + '/api/users.admin.invite',
-        form: {
-          email: req.body.email,
-          token: config.slacktoken,
-          set_active: true
-        }
-      }, function(err, httpResponse, body) {
-        // body looks like:
-        //   {"ok":true}
-        //       or
-        //   {"ok":false,"error":"already_invited"}
-        if (err) { return res.send('Error:' + err); }
-        body = JSON.parse(body);
-        if (body.ok) {
-          res.render('result', {
-            community: config.community,
-            message: 'Success! Check “'+ req.body.email +'” for an invite from Slack.'
-          });
-        } else {
-          var error = body.error;
-          if (error === 'already_invited' || error === 'already_in_team') {
+    function doInvite() {
+      request.post({
+          url: 'https://'+ config.slackUrl + '/api/users.admin.invite',
+          form: {
+            email: req.body.email,
+            token: config.slacktoken,
+            set_active: true
+          }
+        }, function(err, httpResponse, body) {
+          // body looks like:
+          //   {"ok":true}
+          //       or
+          //   {"ok":false,"error":"already_invited"}
+          if (err) { return res.send('Error:' + err); }
+          body = JSON.parse(body);
+          if (body.ok) {
             res.render('result', {
               community: config.community,
-              message: 'Success! You were already invited.<br>' +
-                       'Visit <a href="https://'+ config.slackUrl +'">'+ config.community +'</a>'
+              message: 'Success! Check &ldquo;'+ req.body.email +'&rdquo; for an invite from Slack.'
+            });
+          } else {
+            var error = body.error;
+            if (error === 'already_invited' || error === 'already_in_team') {
+              res.render('result', {
+                community: config.community,
+                message: 'Success! You were already invited.<br>' +
+                        'Visit <a href="https://'+ config.slackUrl +'">'+ config.community +'</a>'
+              });
+              return;
+            } else if (error === 'invalid_email') {
+              error = 'The email you entered is an invalid email.';
+            } else if (error === 'invalid_auth') {
+              error = 'Something has gone wrong. Please contact a system administrator.';
+            }
+
+            res.render('result', {
+              community: config.community,
+              message: 'Failed! ' + error,
+              isFailed: true
             });
-            return;
-          } else if (error === 'invalid_email') {
-            error = 'The email you entered is an invalid email.';
-          } else if (error === 'invalid_auth') {
-            error = 'Something has gone wrong. Please contact a system administrator.';
           }
+        });
+    }
+    if (!!config.recaptchaSiteKey && !!config.recaptchaSecretKey) {
+      request.post({
+        url: 'https://www.google.com/recaptcha/api/siteverify',
+        form: {
+          response: req.body['g-recaptcha-response'],
+          secret: config.recaptchaSecretKey
+        }
+      }, function(err, httpResponse, body) {
+        if (typeof body === "string") {
+          body = JSON.parse(body);
+        }
 
+        if (body.success) {
+          doInvite();
+        } else {
+          error = 'Invalid captcha.';
           res.render('result', {
             community: config.community,
             message: 'Failed! ' + error,
@@ -52,6 +78,9 @@ router.post('/invite', function(req, res) {
           });
         }
       });
+    } else {
+      doInvite();
+    }
   } else {
     var errMsg = [];
     if (!req.body.email) {

+ 4 - 0
views/index.jade

@@ -20,6 +20,10 @@ html
               if tokenRequired
                 input(type="text", name="token", placeholder="#{__('ENTER_TOKEN')}")#slack-token.field
               input(type="submit", value="Join").submit
+              if !!recaptchaSiteKey
+                div(class="g-recaptcha", data-sitekey="#{recaptchaSiteKey}")
+    if !!recaptchaSiteKey
+       script(src='https://www.google.com/recaptcha/api.js')
     script.
       var tokenRequired = #{tokenRequired};
       var form = document.getElementById('join-form');