Skip to content

Commit

Permalink
Adicionando recaptcha-policy, política para validação do captcha.
Browse files Browse the repository at this point in the history
  • Loading branch information
adalcinojunior committed Aug 29, 2019
1 parent 3495448 commit deb207f
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 1 deletion.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ $ eg plugin install https://github.com/nutes-uepb/express-apigw-base-plugin
- issuer: Valid JWT token issuer. (ie 'myapp')
* jwtScopes-policy: Performs the validation of the necessary scopes in the requests. Remembering that each scope must be configuring in each apiPublica path. In case of multiple scopes, the policy verifies the existence of at least one of the scopes.
+ No configuration parameters
* recaptcha-policy: Performs user response validation via the reCaptcha Validation API (https://www.google.com/recaptcha/api/siteverify)
+ Configuration parameters:
- serverKey: String with the secret key provided by reCaptcha. (ie '6L71Sa8SAAsAAPGsl5pHs4hEdT49prH4Au0toPa_') To learn more visit: https://developers.google.com/recaptcha/intro#overview
- recaptchaResponse: Template string with reCaptcha's response. (ie '$ {req.body.recaptchaResponse}')
3 changes: 2 additions & 1 deletion manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ module.exports = {
pluginContext.registerPolicy(require('./policies/auth/auth-policy'))
pluginContext.registerPolicy(require('./policies/body-parser/body-parser-policy'))
pluginContext.registerPolicy(require('./policies/delete-user/delete-user-policy'))
pluginContext.registerPolicy(require('./policies/recaptcha/recaptcha-policy'))
pluginContext.registerCondition(require('./conditions/regex-path-method'))
pluginContext.registerGatewayRoute(require('./routes/middlewares'))
if (fs.existsSync(apiReferencePathFile)) {
pluginContext.registerGatewayRoute(require(apiReferencePathFile))
}
},
// this is for CLI to automatically add to "policies" whitelist in gateway.config
policies: ['jwt-policy', 'jwtScopes-policy', 'auth-policy', 'body-parser-policy', 'delete-user-policy']
policies: ['jwt-policy', 'jwtScopes-policy', 'auth-policy', 'body-parser-policy', 'delete-user-policy', 'recaptcha-policy']
}
22 changes: 22 additions & 0 deletions policies/recaptcha/recaptcha-policy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Recaptcha Policy
*
*/
module.exports = {
name: 'recaptcha-policy',
policy: require('./recaptcha'),
schema: {
name: 'recaptcha',
$id: 'http://express-gateway.io/schemas/policies/recaptcha-policy.json',
type: 'object',
properties: {
serverKey: {
type: 'string'
},
recaptchaResponse: {
type: 'string'
}
},
required: ['serverKey', 'recaptchaResponse']
}
}
96 changes: 96 additions & 0 deletions policies/recaptcha/recaptcha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use strict'

/**
* Recaptcha Policy
*/
const {PassThrough} = require('stream')
let httpClient = require('../../services/http-client')
const logger = require('express-gateway/lib/logger').policy

module.exports = function (actionParams) {
return async (req, res, next) => {
try {
const recaptchaResponse = extractResponse(req, actionParams)
if (recaptchaResponse && recaptchaResponse !== 'undefined') {
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${actionParams.serverKey}
&response=${recaptchaResponse}`
const resultRecaptcha = await httpClient.post(url)
if (!resultRecaptcha || !resultRecaptcha.data || !resultRecaptcha.data.success) {
return res.status(400).send(handlerCaptchaError(resultRecaptcha.data['error-codes']))
}
removeCaptchaResponse(req, actionParams)
}
next()
} catch (e) {
return res.status(400).send(handlerCaptchaError())
}
}
}

/**
* Function used to extract recaptchaResponse value based on template string
* @param req
* @param actionParams
* @returns {*}
*/
function extractResponse(req, actionParams) {
try {
return req.egContext.evaluateAsTemplateString(actionParams.recaptchaResponse)
} catch (err) {
throw new Error('Recaptcha Response not found!')
}

}

/**
* Recaptcha error handler.
* @param captchaErrors
* @returns {{code: number, message: string, descriptions: Array}}
*/
function handlerCaptchaError(captchaErrors) {
const error = {
'code': 400,
'message': 'BAD REQUEST',
'description': 'The captcha response is not valid.'
}
if (!captchaErrors || !captchaErrors.length) return error
if (captchaErrors.includes('missing-input-secret')) {
error.description = 'The secret parameter is missing.'
logger.error('Failed to load recaptcha server secret key. Description: %s', error.description)
return error
}
if (captchaErrors.includes('invalid-input-secret')) {
error.description = 'The secret parameter is invalid or malformed.'
logger.error('Failed to load recaptcha server secret key. Description: %s', error.description)
return error
}

if (captchaErrors.includes('missing-input-response')) error.description = 'The response parameter is missing.'
if (captchaErrors.includes('bad-request')) error.description = 'The request is invalid or malformed.'
if (captchaErrors.includes('invalid-input-response')) {
error.description = 'The response parameter is invalid or malformed.'
}
if (captchaErrors.includes('timeout-or-duplicate')) {
error.description = 'The response is no longer valid: either is too old or has been used previously.'
}
return error
}

/**
* Function used to remove recaptcha Response from request body
* @param req
* @param actionParams
*/
function removeCaptchaResponse(req, actionParams) {
/* removing special characteres */
const recaptchaResponse = actionParams.recaptchaResponse.replace(/\$|{|}/g, '')
const fullKeys = recaptchaResponse.split('.')
const responsekey = fullKeys[fullKeys.length - 1]
/* removing recaptchaResponse from request body */
delete req.body[responsekey]
/* creating new body */
const bodyData = JSON.stringify(req.body)
req.headers['content-length'] = Buffer.byteLength(bodyData)
req.egContext.requestStream = new PassThrough()
req.egContext.requestStream.write(bodyData)
}

0 comments on commit deb207f

Please sign in to comment.