Skip to content
This repository was archived by the owner on Jan 23, 2025. It is now read-only.

Commit 6b5a2e0

Browse files
committed
Module Assembly - TopCoder NodeJS Reset Password API
1 parent 91b0cc5 commit 6b5a2e0

18 files changed

+603
-58
lines changed

actions/resetPassword.js

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,97 @@
11
/*
22
* Copyright (C) 2014 TopCoder Inc., All Rights Reserved.
33
*
4-
* @version 1.0
5-
* @author LazyChild
4+
* @version 1.1
5+
* @author LazyChild, Ghost_141
6+
*
7+
* Changes in 1.1:
8+
* - Implement the Reset Password API instead of mock it.
69
*/
710
"use strict";
811

912
var async = require('async');
13+
var _ = require('underscore');
1014
var BadRequestError = require('../errors/BadRequestError');
1115
var UnauthorizedError = require('../errors/UnauthorizedError');
1216
var ForbiddenError = require('../errors/ForbiddenError');
17+
var NotFoundError = require('../errors/NotFoundError');
18+
var IllegalArgumentError = require('../errors/IllegalArgumentError');
1319

1420
/**
15-
* This is the function that stub reset password
21+
* Reset Password.
1622
*
1723
* @param {Object} api - The api object that is used to access the global infrastructure
1824
* @param {Object} connection - The connection object for the current request
1925
* @param {Function<connection, render>} next - The callback to be called after this function is done
2026
*/
2127
function resetPassword(api, connection, next) {
22-
var result, helper = api.helper;
28+
var result, helper = api.helper, sqlParams, userId, ldapEntryParams, oldPassword,
29+
dbConnectionMap = connection.dbConnectionMap,
30+
token = connection.params.token,
31+
handle = decodeURI(connection.params.handle).toLowerCase(),
32+
newPassword = connection.params.password,
33+
tokenKey = handle + '-' + api.config.general.resetTokenSuffix;
34+
2335
async.waterfall([
24-
function(cb) {
25-
if (connection.params.handle == "nonValid") {
26-
cb(new BadRequestError("The handle you entered is not valid"));
36+
function (cb) {
37+
var error = helper.checkStringPopulated(token, 'token') ||
38+
helper.checkStringPopulated(handle, 'handle') ||
39+
helper.validatePassword(newPassword);
40+
if (error) {
41+
cb(error);
2742
return;
28-
} else if (connection.params.handle == "badLuck") {
29-
cb(new Error("Unknown server error. Please contact support."));
43+
}
44+
sqlParams = {
45+
handle: handle
46+
};
47+
api.dataAccess.executeQuery('get_user_information', sqlParams, dbConnectionMap, cb);
48+
},
49+
function (result, cb) {
50+
if (result.length === 0) {
51+
cb(new NotFoundError('The user is not exist.'));
3052
return;
31-
} else if (connection.params.token == "unauthorized_token") {
32-
cb(new UnauthorizedError("Authentication credentials were missing or incorrect."));
53+
}
54+
userId = result[0].user_id;
55+
oldPassword = helper.decodePassword(result[0].old_password, helper.PASSWORD_HASH_KEY);
56+
sqlParams.handle = result[0].handle;
57+
helper.getCachedValue(tokenKey, cb);
58+
},
59+
function (cache, cb) {
60+
if (!_.isDefined(cache)) {
61+
// The token is either not assigned or is expired.
62+
cb(new BadRequestError('The token is expired, not existed or incorrect. Please apply a new one.'));
3363
return;
34-
} else if (connection.params.token == "forbidden_token") {
35-
cb(new ForbiddenError("The request is understood, but it has been refused or access is not allowed."));
64+
}
65+
if (cache !== token) {
66+
// The token don't match
67+
cb(new IllegalArgumentError('The token is incorrect.'));
68+
return;
69+
}
70+
sqlParams.password = helper.encodePassword(newPassword, helper.PASSWORD_HASH_KEY);
71+
api.dataAccess.executeQuery('update_password', sqlParams, dbConnectionMap, cb);
72+
},
73+
function (count, cb) {
74+
if (count !== 1) {
75+
cb(new Error('password is not updated successfully'));
3676
return;
3777
}
78+
ldapEntryParams = {
79+
userId: userId,
80+
handle: sqlParams.handle,
81+
oldPassword: oldPassword,
82+
newPassword: newPassword
83+
};
84+
api.ldapHelper.updateMemberPasswordLDAPEntry(ldapEntryParams, cb);
85+
},
86+
function (cb) {
87+
// Delete the token from cache system.
88+
api.cache.destroy(tokenKey, function (err) {
89+
cb(err);
90+
});
91+
},
92+
function (cb) {
3893
result = {
39-
"description": "Your password has been reset!"
94+
description: 'Your password has been reset!'
4095
};
4196
cb();
4297
}
@@ -60,16 +115,17 @@ function resetPassword(api, connection, next) {
60115
function generateResetToken(api, connection, next) {
61116
var result, helper = api.helper;
62117
async.waterfall([
63-
function(cb) {
64-
if (connection.params.handle == "nonValid" || connection.params.email == "nonValid@test.com") {
118+
function (cb) {
119+
if (connection.params.handle === "nonValid" || connection.params.email === "nonValid@test.com") {
65120
cb(new BadRequestError("The handle you entered is not valid"));
66121
return;
67-
} else if (connection.params.handle == "badLuck" || connection.params.email == "badLuck@test.com") {
122+
}
123+
if (connection.params.handle === "badLuck" || connection.params.email === "badLuck@test.com") {
68124
cb(new Error("Unknown server error. Please contact support."));
69125
return;
70126
}
71127

72-
if (connection.params.handle == "googleSocial" || connection.params.email == "googleSocial@test.com") {
128+
if (connection.params.handle === "googleSocial" || connection.params.email === "googleSocial@test.com") {
73129
result = {
74130
"socialLogin": "Google"
75131
};
@@ -103,9 +159,16 @@ exports.resetPassword = {
103159
blockedConnectionTypes: [],
104160
outputExample: {},
105161
version: 'v2',
162+
transaction: 'write',
163+
cacheEnabled: false,
164+
databases: ["common_oltp"],
106165
run: function (api, connection, next) {
107-
api.log("Execute resetPassword#run", 'debug');
108-
resetPassword(api, connection, next);
166+
if (connection.dbConnectionMap) {
167+
api.log("Execute resetPassword#run", 'debug');
168+
resetPassword(api, connection, next);
169+
} else {
170+
api.helper.handleNoConnection(api, connection, next);
171+
}
109172
}
110173
};
111174

apiary.apib

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,23 +1049,55 @@ Register a new user.
10491049
{
10501050
"name":"Bad Request",
10511051
"value":"400",
1052-
"description":"This message will explain why the request is invalid or cannot be served."
1052+
"description":"password should be non-null and non-empty string."
10531053
}
10541054

1055-
+ Response 401 (application/json)
1055+
+ Response 400 (application/json)
10561056

10571057
{
1058-
"name":"Unauthorized",
1059-
"value":"401",
1060-
"description":"Authentication credentials were missing or incorrect."
1058+
"name":"Bad Request",
1059+
"value":"400",
1060+
"description":"password must be at least 7 characters in length."
10611061
}
10621062

1063-
+ Response 403 (application/json)
1063+
+ Response 400 (application/json)
10641064

10651065
{
1066-
"name":"Forbidden",
1067-
"value":"403",
1068-
"description":"The request is understood, but it has been refused or access is not allowed."
1066+
"name":"Bad Request",
1067+
"value":"400",
1068+
"description":"password may contain at most 15 characters."
1069+
}
1070+
1071+
+ Response 400 (application/json)
1072+
1073+
{
1074+
"name":"Bad Request",
1075+
"value":"400",
1076+
"description":"Your password may contain only letters, numbers and -_.{}[]()"
1077+
}
1078+
1079+
+ Response 400 (application/json)
1080+
1081+
{
1082+
"name":"Bad Request",
1083+
"value":"400",
1084+
"description":"The token is expired or not existed. Please apply a new one."
1085+
}
1086+
1087+
+ Response 400 (application/json)
1088+
1089+
{
1090+
"name":"Bad Request",
1091+
"value":"400",
1092+
"description":"The token is incorrect."
1093+
}
1094+
1095+
+ Response 404 (application/json)
1096+
1097+
{
1098+
"name":"Not Found",
1099+
"value":"404",
1100+
"description":"The user is not exist."
10691101
}
10701102

10711103
+ Response 500 (application/json)

common/stringUtils.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
/*
2-
* Copyright (C) 2013 TopCoder Inc., All Rights Reserved.
2+
* Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved.
33
*
4-
* Version: 1.0
5-
* Author: TCSASSEMBLER
4+
* Version: 1.1
5+
* Author: TCSASSEMBLER, Ghost_141
6+
* Changes in 1.1:
7+
* - add PUNCTUATION and PASSWORD_ALPHABET.
68
*/
79

810
"use strict";
@@ -18,6 +20,18 @@ var ALPHABET_ALPHA_EN = ALPHABET_ALPHA_LOWER_EN + ALPHABET_ALPHA_UPPER_EN;
1820

1921
var ALPHABET_DIGITS_EN = "0123456789";
2022

23+
/**
24+
* The valid characters for punctuation.
25+
* @since 1.1
26+
*/
27+
var PUNCTUATION = "-_.{}[]()";
28+
29+
/**
30+
* The valid characters for password.
31+
* @since 1.1
32+
*/
33+
var PASSWORD_ALPHABET = ALPHABET_ALPHA_EN + ALPHABET_DIGITS_EN + PUNCTUATION;
34+
2135
/**
2236
* Checks if string has all its characters in alphabet given.
2337
*
@@ -42,4 +56,6 @@ exports.containsOnly = function (string, alphabet) {
4256
exports.ALPHABET_ALPHA_UPPER_EN = ALPHABET_ALPHA_UPPER_EN;
4357
exports.ALPHABET_ALPHA_LOWER_EN = ALPHABET_ALPHA_LOWER_EN;
4458
exports.ALPHABET_ALPHA_EN = ALPHABET_ALPHA_EN;
45-
exports.ALPHABET_DIGITS_EN = ALPHABET_DIGITS_EN;
59+
exports.ALPHABET_DIGITS_EN = ALPHABET_DIGITS_EN;
60+
exports.PUNCTUATION = PUNCTUATION;
61+
exports.PASSWORD_ALPHABET = PASSWORD_ALPHABET;

config.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved.
33
*
44
* @author vangavroche, Ghost_141, kurtrips, Sky_, isv
5-
* @version 1.12
5+
* @version 1.13
66
* changes in 1.1:
77
* - add defaultCacheLifetime parameter
88
* changes in 1.2:
@@ -30,6 +30,9 @@
3030
* - added designSubmissionsBasePath for design submissions
3131
* changes in 1.12:
3232
* - add defaultUserCacheLifetime property.
33+
* Changes in 1.13:
34+
* - add minPasswordLength and maxPasswordLength
35+
* - add resetTokenSuffix
3336
*/
3437
"use strict";
3538

@@ -71,6 +74,9 @@ config.general = {
7174
defaultCacheLifetime : process.env.CACHE_EXPIRY || 1000 * 60 * 10, //10 min default
7275
defaultAuthMiddlewareCacheLifetime : process.env.AUTH_MIDDLEWARE_CACHE_EXPIRY || 1000 * 60 * 10, //10 min default
7376
defaultUserCacheLifetime: process.env.USER_CACHE_EXPIRY || 1000 * 60 * 60 * 24, //24 hours default
77+
resetTokenSuffix: 'reset-token',
78+
minPasswordLength: 8,
79+
maxPasswordLength: 30,
7480
cachePrefix: '',
7581
oauthClientId: process.env.OAUTH_CLIENT_ID || "CMaBuwSnY0Vu68PLrWatvvu3iIiGPh7t",
7682
//auth0 secret is encoded in base64!

deploy/ci.sh

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#!/bin/bash
22

33
#
4-
# Copyright (C) 2013 TopCoder Inc., All Rights Reserved.
4+
# Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved.
55
#
6-
# Version: 1.0
7-
# Author: vangavroche, delemach
6+
# Version: 1.1
7+
# Author: vangavroche, delemach, Ghost_141
8+
# Changes in 1.1
9+
# - add REDIS_HOST and REDIS_PORT.
810
#
911
export CACHE_EXPIRY=-1
1012

@@ -67,3 +69,7 @@ export JIRA_USERNAME=api_test
6769
export JIRA_PASSWORD=8CDDp6BHLtUeUdD
6870

6971
export ACTIONHERO_CONFIG=./config.js
72+
73+
# Used in api cache.
74+
export REDIS_HOST=localhost
75+
export REDIS_PORT=6379

deploy/development.bat

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
REM
33
REM Copyright (C) 2014 TopCoder Inc., All Rights Reserved.
44
REM
5-
REM Version: 1.0
6-
REM Author: TrePe
5+
REM Version: 1.1
6+
REM Author: TrePe, Ghost_141
77
REM
8+
REM Changes in 1.1
9+
REM - Add REDIS_PORT and REDIS_HOST.
810

911
REM tests rely on caching being off. But set this to a real value (or remove) while coding.
1012

@@ -66,3 +68,6 @@ set JIRA_PASSWORD=8CDDp6BHLtUeUdD
6668

6769
set ACTIONHERO_CONFIG=./config.js
6870

71+
REM Used in API cache
72+
set REDIS_HOST=localhost
73+
set REDIS_PORT=6379

deploy/development.sh

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#!/bin/bash
22

33
#
4-
# Copyright (C) 2013 TopCoder Inc., All Rights Reserved.
4+
# Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved.
55
#
6-
# Version: 1.1
7-
# Author: vangavroche, TCSASSEMBLER
6+
# Version: 1.2
7+
# Author: vangavroche, Ghost_141
88
# changes in 1.1:
99
# - add JIRA_USERNAME and JIRA_PASSWORD
10+
# changes in 1.2:
11+
# - add REDIS_HOST and REDIS_PORT.
1012
#
1113

1214
# tests rely on caching being off. But set this to a real value (or remove) while coding.
@@ -67,4 +69,8 @@ export TIMEOUT=3000
6769
export JIRA_USERNAME=api_test
6870
export JIRA_PASSWORD=8CDDp6BHLtUeUdD
6971

72+
# Used in api cache.
73+
export REDIS_HOST=localhost
74+
export REDIS_PORT=6379
75+
7076
export ACTIONHERO_CONFIG=./config.js

0 commit comments

Comments
 (0)