Skip to content

Commit 5655a29

Browse files
committed
Merge branch 'feature/recaptcha-integration' into dev
2 parents e44eb0b + 90ceb05 commit 5655a29

File tree

11 files changed

+97
-2
lines changed

11 files changed

+97
-2
lines changed

configs/project/client.js

+1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ module.exports = {
88
trackingID: 'UA-86112397-1',
99
},
1010
},
11+
recaptcha: require('./recaptcha/client'),
1112
};

configs/project/recaptcha/client.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module.exports = {
2+
development: {
3+
siteKey: '6LfQGgoUAAAAAANHp6AvQOYD81JU9GnThpiIK7pH',
4+
},
5+
test: {
6+
siteKey: '6LfQGgoUAAAAAANHp6AvQOYD81JU9GnThpiIK7pH',
7+
},
8+
production: {
9+
siteKey: '6LeoHAoUAAAAAHKlo43OuPREJb22GLmik2HSaFC1',
10+
},
11+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module.exports = {
2+
development: {
3+
secretKey: 'akb48akb48akb48akb48akb48akb48akb48akb48',
4+
},
5+
test: {
6+
secretKey: 'akb48akb48akb48akb48akb48akb48akb48akb48',
7+
},
8+
production: {
9+
secretKey: 'akc49akc49akc49akc49akc49akc49akc49akc49',
10+
},
11+
};

configs/project/server.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ if (process.env.TRAVIS) {
1818
facebook: require('./passportStrategy/facebook/credential'),
1919
linkedin: require('./passportStrategy/linkedin/credential'),
2020
},
21+
recaptcha: require('./recaptcha/credential'),
2122
};
2223
}

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
"react-bootstrap": "^0.30.5",
116116
"react-dom": "^15.0.2",
117117
"react-ga": "^2.1.2",
118+
"react-google-recaptcha": "^0.5.4",
118119
"react-helmet": "^3.0.2",
119120
"react-intl": "^2.1.2",
120121
"react-native": "^0.31.0",

src/common/components/forms/RegisterForm.js

+11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import userAPI from '../../api/user';
88
import { validateForm } from '../../actions/formActions';
99
import { pushErrors } from '../../actions/errorActions';
1010
import { Form, FormField, FormFooter } from '../utils/BsForm';
11+
import configs from '../../../../configs/project/client';
1112

1213
const validate = (values) => {
1314
const errors = {};
@@ -24,6 +25,10 @@ const validate = (values) => {
2425
errors.password = 'Required';
2526
}
2627

28+
if (configs.recaptcha && !values.recaptcha) {
29+
errors.recaptcha = 'Required';
30+
}
31+
2732
return errors;
2833
};
2934

@@ -90,6 +95,12 @@ class RegisterForm extends Component {
9095
type="password"
9196
placeholder="Password"
9297
/>
98+
<Field
99+
label=" "
100+
name="recaptcha"
101+
component={FormField}
102+
type="recaptcha"
103+
/>
93104
<FormFooter>
94105
<Button
95106
type="submit"

src/common/components/utils/BsForm.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import BsFormGroup from 'react-bootstrap/lib/FormGroup';
44
import Col from 'react-bootstrap/lib/Col';
55
import ControlLabel from 'react-bootstrap/lib/ControlLabel';
66
import HelpBlock from 'react-bootstrap/lib/HelpBlock';
7+
import Recaptcha from 'react-google-recaptcha';
8+
import configs from '../../../../configs/project/client';
79

810
class BsForm extends Component {
911
getChildContext() {
@@ -65,7 +67,20 @@ let BsFormField = ({
6567
let isShowError = meta && meta.touched && meta.error;
6668

6769
let formControl = null;
68-
if (options) {
70+
if (type === 'recaptcha') {
71+
// ref:
72+
// - <https://github.com/erikras/redux-form/issues/1880>
73+
/* eslint-disable */
74+
formControl = configs.recaptcha ? (
75+
<Recaptcha
76+
sitekey={configs.recaptcha[process.env.NODE_ENV].siteKey}
77+
onChange={input.onChange}
78+
/>
79+
) : (
80+
<pre>Recaptcha is disabled</pre>
81+
);
82+
/* eslint-enable */
83+
} else if (options) {
6984
// ref: <https://github.com/erikras/redux-form/issues/1857#issuecomment-249890206>
7085
formControl = (
7186
options.map((option) => (

src/common/constants/ErrorCodes.js

+1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ export default {
88
LOCALE_NOT_SUPPORTED: 'LOCALE_NOT_SUPPORTED',
99
USER_TOKEN_EXPIRATION: 'USER_TOKEN_EXPIRATION',
1010
ODM_VALIDATION: 'ODM_VALIDATION',
11+
INVALID_RECAPTCHA: 'INVALID_RECAPTCHA',
1112
SOCIAL_AUTH_FAIL: 'SOCIAL_AUTH_FAIL',
1213
};

src/common/constants/Errors.js

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ export default {
1919
title: 'Database Validation Failed',
2020
detail: 'The data is invalid.',
2121
},
22+
[ErrorCodes.INVALID_RECAPTCHA]: {
23+
code: ErrorCodes.INVALID_RECAPTCHA,
24+
status: 400,
25+
title: 'Invalid Recaptcha',
26+
detail: 'The value of recaptcha is invalid.',
27+
},
2228
[ErrorCodes.STATE_PRE_FETCHING_FAIL]: {
2329
code: ErrorCodes.STATE_PRE_FETCHING_FAIL,
2430
status: 500,
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import superagent from 'superagent';
2+
import Errors from '../../common/constants/Errors';
3+
import serverConfigs from '../../../configs/project/server';
4+
import clientConfigs from '../../../configs/project/client';
5+
6+
export default (req, res, next) => {
7+
if (process.env.NODE_ENV === 'test' || !clientConfigs.recaptcha) {
8+
return next();
9+
}
10+
superagent
11+
.post('https://www.google.com/recaptcha/api/siteverify')
12+
.type('form')
13+
.send({
14+
secret: serverConfigs.recaptcha[process.env.NODE_ENV].secretKey,
15+
response: req.body.recaptcha,
16+
})
17+
.end((err, { body } = {}) => {
18+
if (err) {
19+
res.pushError(Errors.UNKNOWN_EXCEPTION, {
20+
meta: err,
21+
});
22+
return res.errors();
23+
}
24+
if (!body.success) {
25+
res.pushError(Errors.INVALID_RECAPTCHA, {
26+
meta: body['error-codes'],
27+
});
28+
return res.errors();
29+
}
30+
next();
31+
});
32+
};

src/server/routes/api.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import configs from '../../../configs/project/server';
22
import Roles from '../../common/constants/Roles';
33
import bodyParser from '../middlewares/bodyParser';
4+
import verifyRecaptcha from '../middlewares/verifyRecaptcha';
45
import authRequired from '../middlewares/authRequired';
56
import roleRequired from '../middlewares/roleRequired';
67
import fileUpload from '../middlewares/fileUpload';
@@ -16,7 +17,11 @@ export default ({ app }) => {
1617
roleRequired([Roles.ADMIN]),
1718
userController.list
1819
);
19-
app.post('/api/users', bodyParser.json, userController.create);
20+
app.post('/api/users',
21+
bodyParser.json,
22+
verifyRecaptcha,
23+
userController.create
24+
);
2025
app.post('/api/users/login', bodyParser.json, userController.login);
2126
app.get('/api/users/logout', userController.logout);
2227
app.get('/api/users/me', authRequired, userController.show);

0 commit comments

Comments
 (0)