Refactor reCaptcha
Added reCaptcha score in config.json
Updated doc
MODIFY
README.md
README.md
@@ -242,35 +242,20 @@ This application displays the charging stations with their statuses, charging cu
To set the end point, fill the following information in the **config.json** file
-#### Secure Configuration (SSL)
-
-```json
- "CentralSystemRestService": {
- "protocol": "https",
- "host": "localhost",
- "port": 443,
- "sslKey": "ssl/64933587-localhost.key",
- "sslCert": "ssl/64933587-localhost.cert",
- "sslCa": [],
- "userTokenKey": "MySecureKeyToEncodeTokenAuth",
- "userTokenLifetimeHours": 12,
- "userDemoTokenLifetimeDays": 365,
- "userTechnicalTokenLifetimeDays": 365,
- "debug": false
- }
-```
-
-#### Simple Configuration
+#### Configuration
```json
"CentralSystemRestService": {
"protocol": "http",
- "host": "localhost",
+ "host": "YOUR_HOST",
"port": 80,
- "userTokenKey": "YourSecureKeyToEncodeTokenAuth",
+ "userTokenKey": "YOUR_JWT_PRIVATE_KEY",
"userTokenLifetimeHours": 12,
- "userDemoTokenLifetimeDays": 365,
- "userTechnicalTokenLifetimeDays": 365,
+ "userDemoTokenLifetimeDays": 360,
+ "userTechnicalTokenLifetimeDays": 180,
+ "passwordWrongNumberOfTrial": 3,
+ "passwordBlockedWaitTimeMin": 5,
+ "captchaSecretKey": "YOUR_CAPTCHA_SECRET",
"debug": false
}
```

MODIFY
docker/config-standalone.json
docker/config-standalone.json
"passwordWrongNumberOfTrial": 3,
"passwordBlockedWaitTimeMin": 5,
"captchaSecretKey": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+ "captchaScore": 0.25,
"debug": false
},
"CentralSystemFrontEnd": {

MODIFY
docker/config.json
docker/config.json
"passwordWrongNumberOfTrial": 3,
"passwordBlockedWaitTimeMin": 5,
"captchaSecretKey": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+ "captchaScore": 0.25,
"debug": false
},
"CentralSystemFrontEnd": {

MODIFY
package-lock.json
package-lock.json
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
- "node_modules/0x/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"node_modules/0x/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"pako": "~1.0.5"
}
},
- "node_modules/browserify/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"node_modules/browserify/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"typedarray": "^0.0.6"
}
},
- "node_modules/concat-stream/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
"node_modules/concat-stream/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "^2.0.2"
}
},
- "node_modules/duplexer2/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
"node_modules/duplexer2/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"from2": "^2.0.3"
}
},
- "node_modules/from2/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"node_modules/from2/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==",
"dev": true
},
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
"node_modules/isemail": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz",
"node": ">= 0.8.0"
}
},
- "node_modules/module-deps/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"node_modules/module-deps/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "^2.0.5"
}
},
- "node_modules/multistream/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"node_modules/multistream/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "^2.0.2"
}
},
- "node_modules/read-only-stream/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"node_modules/read-only-stream/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"source-map": "~0.6.1"
}
},
- "node_modules/static-module/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
"node_modules/static-module/node_modules/levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"readable-stream": "^2.0.2"
}
},
- "node_modules/stream-combiner2/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"node_modules/stream-combiner2/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "^2.0.2"
}
},
- "node_modules/stream-splicer/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"node_modules/stream-splicer/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"node": ">=4.0.0"
}
},
- "node_modules/stream-template/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"node_modules/stream-template/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "2"
}
},
- "node_modules/streaming-json-stringify/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"node_modules/streaming-json-stringify/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"xtend": "~4.0.1"
}
},
- "node_modules/through2/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
"node_modules/through2/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"xtend": "^4.0.0"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"typedarray": "^0.0.6"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "^2.0.2"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "^2.0.0"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==",
"dev": true
},
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
"isemail": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz",
"xtend": "^4.0.0"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "^2.0.5"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "^2.0.2"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"source-map": "~0.6.1"
}
},
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"readable-stream": "^2.0.2"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "^2.0.2"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "^2.3.6"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"readable-stream": "2"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"xtend": "~4.0.1"
},
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",

MODIFY
src/assets/config-template-http.json
src/assets/config-template-http.json
"passwordWrongNumberOfTrial": 3,
"passwordBlockedWaitTimeMin": 5,
"captchaSecretKey": "YourCaptchaSecretKey",
+ "captchaScore": 0.25,
"debug": false
},
"CentralSystemFrontEnd": {

MODIFY
src/assets/config-template-https.json
src/assets/config-template-https.json
"passwordWrongNumberOfTrial": 3,
"passwordBlockedWaitTimeMin": 5,
"captchaSecretKey": "YourCaptchaSecretKey",
+ "captchaScore": 0.25,
"debug": false
},
"CentralSystemFrontEnd": {

MODIFY
src/assets/schemas/configuration/configuration.json
src/assets/schemas/configuration/configuration.json
"captchaSecretKey": {
"type": "string"
},
+ "captchaScore": {
+ "type": "number"
+ },
"debug": {
"type": "boolean"
}

MODIFY
src/server/rest/v1/service/AuthService.ts
src/server/rest/v1/service/AuthService.ts
@@ -6,7 +6,6 @@ import User, { UserRole, UserStatus } from '../../../../types/User';
import AppError from '../../../../exception/AppError';
import AuthValidator from '../validator/AuthValidator';
import Authorizations from '../../../../authorization/Authorizations';
-import AxiosFactory from '../../../../utils/AxiosFactory';
import BillingFactory from '../../../../integration/billing/BillingFactory';
import Configuration from '../../../../utils/Configuration';
import Constants from '../../../../utils/Constants';
@@ -29,15 +28,15 @@ import jwt from 'jsonwebtoken';
import moment from 'moment';
import passport from 'passport';
-const _centralSystemRestConfig = Configuration.getCentralSystemRestServiceConfig();
+const centralSystemRestConfig = Configuration.getCentralSystemRestServiceConfig();
let jwtOptions;
// Init JWT auth options
-if (_centralSystemRestConfig) {
+if (centralSystemRestConfig) {
// Set
jwtOptions = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
- secretOrKey: _centralSystemRestConfig.userTokenKey
+ secretOrKey: centralSystemRestConfig.userTokenKey
};
// Use
passport.use(new Strategy(jwtOptions, (jwtPayload, done) =>
@@ -76,7 +75,7 @@ export default class AuthService {
UtilsService.assertObjectExists(action, user, `User with email '${filteredRequest.email}' does not exist`,
MODULE_NAME, 'handleLogIn', req.user);
// Check if the number of trials is reached
- if (user.passwordWrongNbrTrials >= _centralSystemRestConfig.passwordWrongNumberOfTrial) {
+ if (user.passwordWrongNbrTrials >= centralSystemRestConfig.passwordWrongNumberOfTrial) {
// Check if the user is still locked
if (user.status === UserStatus.LOCKED) {
// Yes: Check date to reset pass
@@ -138,25 +137,9 @@ export default class AuthService {
});
}
req.user = { tenantID: tenant.id };
- // Check Captcha
- const recaptchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${_centralSystemRestConfig.captchaSecretKey}&response=${filteredRequest.captcha}&remoteip=${req.connection.remoteAddress}`;
- const response = await AxiosFactory.getAxiosInstance(tenant).get(recaptchaURL);
- if (!response.data.success) {
- throw new AppError({
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'The captcha is invalid',
- module: MODULE_NAME,
- method: 'handleRegisterUser'
- });
- }
- if (response.data.score < 0.25) {
- throw new AppError({
- errorCode: HTTPError.GENERAL_ERROR,
- message: `The captcha score is too low, got ${response.data.score as string} but expected 0.25`,
- module: MODULE_NAME,
- method: 'handleRegisterUser'
- });
- }
+ // Check reCaptcha
+ await UtilsService.checkReCaptcha(tenant, action, 'handleRegisterUser',
+ centralSystemRestConfig, filteredRequest.captcha, req.connection.remoteAddress);
// Check Mandatory field
UtilsService.checkIfUserValid(filteredRequest as User, null, req);
// Check email
@@ -271,24 +254,9 @@ export default class AuthService {
method: 'checkAndSendResetPasswordConfirmationEmail'
});
}
- // Check captcha
- const recaptchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${_centralSystemRestConfig.captchaSecretKey}&response=${filteredRequest.captcha}&remoteip=${req.connection.remoteAddress}`;
- const response = await AxiosFactory.getAxiosInstance(tenant).get(recaptchaURL);
- if (!response.data.success) {
- throw new AppError({
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'The reCaptcha is invalid',
- module: MODULE_NAME,
- method: 'checkAndSendResetPasswordConfirmationEmail'
- });
- } else if (response.data.score < 0.5) {
- throw new AppError({
- errorCode: HTTPError.GENERAL_ERROR,
- message: `The reCaptcha score is too low, got ${response.data.score as string} and expected to be >= 0.5`,
- module: MODULE_NAME,
- method: 'checkAndSendResetPasswordConfirmationEmail'
- });
- }
+ // Check reCaptcha
+ await UtilsService.checkReCaptcha(tenant, action, 'checkAndSendResetPasswordConfirmationEmail',
+ centralSystemRestConfig, filteredRequest.captcha, req.connection.remoteAddress);
// Generate a new password
const user = await UserStorage.getUserByEmail(tenant, filteredRequest.email);
UtilsService.assertObjectExists(action, user, `User with email '${filteredRequest.email}' does not exist`,
@@ -561,26 +529,9 @@ export default class AuthService {
action: action
});
}
- // Is valid captcha?
- const recaptchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${_centralSystemRestConfig.captchaSecretKey}&response=${filteredRequest.captcha}&remoteip=${req.connection.remoteAddress}`;
- const response = await AxiosFactory.getAxiosInstance(tenant).get(recaptchaURL);
- if (!response.data.success) {
- throw new AppError({
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'The captcha is invalid',
- module: MODULE_NAME,
- method: 'handleResendVerificationEmail',
- action: action
- });
- } else if (response.data.score < 0.5) {
- throw new AppError({
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'The captcha is too low',
- module: MODULE_NAME,
- method: 'handleResendVerificationEmail',
- action: action
- });
- }
+ // Check reCaptcha
+ await UtilsService.checkReCaptcha(tenant, action, 'handleResendVerificationEmail',
+ centralSystemRestConfig, filteredRequest.captcha, req.connection.remoteAddress);
// Is valid email?
const user = await UserStorage.getUserByEmail(tenant, filteredRequest.email);
UtilsService.assertObjectExists(action, user, `User with email '${filteredRequest.email}' does not exist`,
@@ -650,7 +601,7 @@ export default class AuthService {
}
const passwordWrongNbrTrials = user.passwordWrongNbrTrials + 1;
// Check if the number of trial is reached
- if (passwordWrongNbrTrials >= _centralSystemRestConfig.passwordWrongNumberOfTrial) {
+ if (passwordWrongNbrTrials >= centralSystemRestConfig.passwordWrongNumberOfTrial) {
// Too many attempts, lock user
// Save User Status
await UserStorage.saveUserStatus(tenant, user.id, UserStatus.LOCKED);
@@ -658,7 +609,7 @@ export default class AuthService {
await UserStorage.saveUserPassword(tenant, user.id,
{
passwordWrongNbrTrials,
- passwordBlockedUntil: moment().add(_centralSystemRestConfig.passwordBlockedWaitTimeMin, 'm').toDate()
+ passwordBlockedUntil: moment().add(centralSystemRestConfig.passwordBlockedWaitTimeMin, 'm').toDate()
});
throw new AppError({
errorCode: HTTPError.USER_ACCOUNT_LOCKED_ERROR,
@@ -673,7 +624,7 @@ export default class AuthService {
await UserStorage.saveUserPassword(tenant, user.id, { passwordWrongNbrTrials });
throw new AppError({
errorCode: HTTPError.OBJECT_DOES_NOT_EXIST_ERROR,
- message: `User failed to log in, ${_centralSystemRestConfig.passwordWrongNumberOfTrial - user.passwordWrongNbrTrials} trial(s) remaining`,
+ message: `User failed to log in, ${centralSystemRestConfig.passwordWrongNumberOfTrial - user.passwordWrongNbrTrials} trial(s) remaining`,
module: MODULE_NAME,
method: 'checkUserLogin',
action: action,
@@ -714,15 +665,15 @@ export default class AuthService {
// Role Demo?
if (Authorizations.isDemo(user)) {
token = jwt.sign(payload, jwtOptions.secretOrKey, {
- expiresIn: _centralSystemRestConfig.userDemoTokenLifetimeDays * 24 * 3600
+ expiresIn: centralSystemRestConfig.userDemoTokenLifetimeDays * 24 * 3600
});
} else if (user.technical) {
token = jwt.sign(payload, jwtOptions.secretOrKey, {
- expiresIn: _centralSystemRestConfig.userTechnicalTokenLifetimeDays * 24 * 3600
+ expiresIn: centralSystemRestConfig.userTechnicalTokenLifetimeDays * 24 * 3600
});
} else {
token = jwt.sign(payload, jwtOptions.secretOrKey, {
- expiresIn: _centralSystemRestConfig.userTokenLifetimeHours * 3600
+ expiresIn: centralSystemRestConfig.userTokenLifetimeHours * 3600
});
}
// Return it

MODIFY
src/server/rest/v1/service/UtilsService.ts
src/server/rest/v1/service/UtilsService.ts
@@ -12,7 +12,9 @@ import Asset from '../../../../types/Asset';
import AssetStorage from '../../../../storage/mongodb/AssetStorage';
import AuthorizationService from './AuthorizationService';
import Authorizations from '../../../../authorization/Authorizations';
+import AxiosFactory from '../../../../utils/AxiosFactory';
import CarStorage from '../../../../storage/mongodb/CarStorage';
+import CentralSystemRestServiceConfiguration from '../../../../types/configuration/CentralSystemRestServiceConfiguration';
import { ChargingProfile } from '../../../../types/ChargingProfile';
import ChargingStationStorage from '../../../../storage/mongodb/ChargingStationStorage';
import Company from '../../../../types/Company';
@@ -51,6 +53,31 @@ const MODULE_NAME = 'UtilsService';
export default class UtilsService {
+ public static async checkReCaptcha(tenant: Tenant, action: ServerAction, method: string,
+ centralSystemRestConfig: CentralSystemRestServiceConfiguration, captcha: string, remoteAddress: string): Promise<void> {
+ const recaptchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${centralSystemRestConfig.captchaSecretKey}&response=${captcha}&remoteip=${remoteAddress}`;
+ const response = await AxiosFactory.getAxiosInstance(tenant).get(recaptchaURL);
+ if (!response.data.success) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ module: MODULE_NAME, action, method,
+ message: 'The Captcha is invalid',
+ });
+ }
+ if (response.data.score < centralSystemRestConfig.captchaScore) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ module: MODULE_NAME, action, method,
+ message: `The Captcha score is too low, got ${response.data.score as string} but expected ${centralSystemRestConfig.captchaScore}`,
+ });
+ }
+ await Logging.logDebug({
+ tenantID: tenant?.id,
+ module: MODULE_NAME, action, method,
+ message: `The Captcha score is ${response.data.score as string} (score limit is ${centralSystemRestConfig.captchaScore})`,
+ });
+ }
+
public static async checkAndGetChargingStationAuthorization(tenant: Tenant, userToken: UserToken, chargingStationID: string,
action: ServerAction, entityData?: EntityData, additionalFilters: Record<string, any> = {}, applyProjectFields = false): Promise<ChargingStation> {
// Check static auth for reading Charging Station

MODIFY
src/types/configuration/CentralSystemRestServiceConfiguration.ts
src/types/configuration/CentralSystemRestServiceConfiguration.ts
@@ -8,5 +8,6 @@ export default interface CentralSystemRestServiceConfiguration extends CentralSy
passwordWrongNumberOfTrial: number;
passwordBlockedWaitTimeMin: number;
captchaSecretKey: string;
+ captchaScore: number;
debug: boolean;
}

MODIFY
src/utils/Configuration.ts
src/utils/Configuration.ts
@@ -99,6 +99,9 @@ export default class Configuration {
public static getCentralSystemRestServiceConfig(): CentralSystemRestServiceConfiguration {
const centralSystemRestService = Configuration.getConfig().CentralSystemRestService;
if (!Configuration.isUndefined('CentralSystemRestService', centralSystemRestService)) {
+ if (Configuration.isUndefined('CentralSystemRestService.captchaScore', centralSystemRestService.captchaScore)) {
+ centralSystemRestService.captchaScore = 0.25;
+ }
return centralSystemRestService;
}
}

Refactor reCaptcha
Added reCaptcha score in config.json
Updated doc