Skip to content
This repository has been archived by the owner on Apr 3, 2019. It is now read-only.

Commit

Permalink
fix(lock): add ip lock test
Browse files Browse the repository at this point in the history
  • Loading branch information
vladikoff committed Apr 3, 2016
1 parent add5795 commit 30f8a5d
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 99 deletions.
4 changes: 3 additions & 1 deletion lib/bans/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ var RATE_LIMIT_INTERVAL_MS = config.limits.rateLimitIntervalSeconds * 1000
var MAX_EMAILS = config.limits.emails
var BAD_LOGIN_LOCKOUT = config.limits.badLoginLockout
var BAD_LOGIN_LOCKOUT_INTERVAL_MS = config.limits.badLoginLockoutIntervalSeconds * 1000
var MAX_BAD_LOGINS_PER_IP = config.limits.maxBadLoginsPerIp
var MAX_ACCOUNT_STATUS_CHECK = config.limits.maxAccountStatusCheck

var EmailRecord = require('../email_record')(RATE_LIMIT_INTERVAL_MS, BLOCK_INTERVAL_MS, BAD_LOGIN_LOCKOUT_INTERVAL_MS, MAX_EMAILS, BAD_LOGIN_LOCKOUT)
var IpRecord = require('../ip_record')(BLOCK_INTERVAL_MS)
var IpRecord = require('../ip_record')(BLOCK_INTERVAL_MS, RATE_LIMIT_INTERVAL_MS, MAX_BAD_LOGINS_PER_IP, MAX_ACCOUNT_STATUS_CHECK)

module.exports = function (mc, log) {

Expand Down
2 changes: 2 additions & 0 deletions test/memcache-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ var config = {
limits: {
blockIntervalSeconds: 1,
rateLimitIntervalSeconds: 1,
maxAccountStatusCheck: Number(process.env.MAX_ACCOUNT_STATUS_CHECK) || 5,
maxEmails: 3,
maxBadLogins: 2,
maxBadLoginsPerIp: Number(process.env.MAX_BAD_LOGINS_PER_IP) || 3,
badLoginLockout: 3,
badLoginLockoutIntervalSeconds: 20
}
Expand Down
101 changes: 3 additions & 98 deletions test/remote/too_many_bad_logins.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ var config = {
},
limits: {
rateLimitIntervalSeconds: 1,
badLoginLockoutIntervalSeconds: 1,
maxBadLoginsPerIp: 8
badLoginLockoutIntervalSeconds: 1
}
}

Expand Down Expand Up @@ -50,7 +49,7 @@ var client = restify.createJsonClient({
})

test(
'too many failed logins from the same IP, against the same email',
'too many failed logins from the same IP',
function (t) {
client.post('/failedLoginAttempt', { email: TEST_EMAIL, ip: TEST_IP },
function (err, req, res, obj) {
Expand Down Expand Up @@ -120,7 +119,7 @@ test(
)

test(
'too many failed logins from different IPs, against the same email',
'too many failed logins from different IPs',
function (t) {
client.post('/failedLoginAttempt', { email: TEST_EMAIL, ip: '192.0.2.10' },
function (err, req, res, obj) {
Expand Down Expand Up @@ -279,100 +278,6 @@ test(
}
)

test(
'clear everything',
function (t) {
mcHelper.clearEverything(
function (err) {
t.notOk(err, 'no errors were returned')
t.end()
}
)
}
)

test('too many failed logins from the same IP, against different emails',
function (t) {
client.post('/failedLoginAttempt', { email: 'one-' + TEST_EMAIL, ip: TEST_IP },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'failed login 1')
t.equal(obj.lockout, false, 'not locked out')

client.post('/failedLoginAttempt', { email: 'two-' + TEST_EMAIL, ip: TEST_IP },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'failed login 2')
t.equal(obj.lockout, false, 'not locked out')

client.post('/failedLoginAttempt', { email: 'three-' + TEST_EMAIL, ip: TEST_IP },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'failed login 3')
t.equal(obj.lockout, false, 'not locked out')

client.post('/failedLoginAttempt', { email: 'four-' + TEST_EMAIL, ip: TEST_IP },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'failed login 4')
t.equal(obj.lockout, false, 'not locked out')

client.post('/failedLoginAttempt', { email: 'five-' + TEST_EMAIL, ip: TEST_IP },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'failed login 5')
t.equal(obj.lockout, false, 'locked out')

client.post('/failedLoginAttempt', { email: 'six-' + TEST_EMAIL, ip: TEST_IP },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'failed login 6')
t.equal(obj.lockout, false, 'locked out')

client.post('/failedLoginAttempt', { email: 'seven-' + TEST_EMAIL, ip: TEST_IP },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'failed login 7')
t.equal(obj.lockout, false, 'not locked out')

client.post('/failedLoginAttempt', { email: 'eight-' + TEST_EMAIL, ip: TEST_IP },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'failed login 8')
t.equal(obj.lockout, false, 'not locked out')

client.post('/check', { email: TEST_EMAIL, ip: TEST_IP, action: 'accountLogin' },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'check succeeded')
t.equal(obj.block, false, 'not blocked')

client.post('/failedLoginAttempt', { email: 'nine-' + TEST_EMAIL, ip: TEST_IP },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'failed login 9')
t.equal(obj.lockout, false, 'not locked out')

client.post('/check', { email: TEST_EMAIL, ip: TEST_IP, action: 'accountLogin' },
function (err, req, res, obj) {
t.equal(res.statusCode, 200, 'check succeeded')
t.equal(obj.lockout, false, 'blocked')
t.end()
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)

test(
'teardown',
function (t) {
Expand Down
122 changes: 122 additions & 0 deletions test/remote/too_many_login_checks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */

// Override limit values for testing
process.env.MAX_BAD_LOGINS_PER_IP = 2
process.env.RATE_LIMIT_INTERVAL_SECONDS = 2

var test = require('tap').test
var TestServer = require('../test_server')
var Promise = require('bluebird')
var restify = Promise.promisifyAll(require('restify'))
var mcHelper = require('../memcache-helper')

var TEST_IP = '192.0.2.1'
var ACCOUNT_LOGIN = 'accountLogin'

var config = {
listen: {
port: 7000
}
}

var testServer = new TestServer(config)

var client = restify.createJsonClient({
url: 'http://127.0.0.1:' + config.listen.port
})

Promise.promisifyAll(client)

test(
'startup',
function (t) {
testServer.start(function (err) {
t.type(testServer.server, 'object', 'test server was started')
t.notOk(err, 'no errors were returned')
t.end()
})
}
)

test(
'clear everything',
function (t) {
mcHelper.clearEverything(
function (err) {
t.notOk(err, 'no errors were returned')
t.end()
}
)
}
)

test(
'/check `accountLogin` with different emails',
function (t) {

// Send requests until throttled
return client.postAsync('/failedLoginAttempt', { ip: TEST_IP, email: 'test-fail1@example.com', action: ACCOUNT_LOGIN })
.spread(function(req, res, obj){
t.equal(res.statusCode, 200, 'failed login 1')
t.equal(obj.lockout, false, 'not locked out')

return client.postAsync('/failedLoginAttempt', { ip: TEST_IP, email: 'test-fail2@example.com', action: ACCOUNT_LOGIN })
})
.spread(function(req, res, obj){
t.equal(res.statusCode, 200, 'failed login 2')
t.equal(obj.lockout, false, 'not locked out')

return client.postAsync('/check', { ip: TEST_IP, email: 'test1@example.com', action: ACCOUNT_LOGIN })
})
.spread(function(req, res, obj){
t.equal(res.statusCode, 200, 'returns a 200')
t.equal(obj.block, false, 'not rate limited')

return client.postAsync('/failedLoginAttempt', { ip: TEST_IP, email: 'test-fail3@example.com', action: ACCOUNT_LOGIN })
})
.spread(function(req, res, obj){
t.equal(res.statusCode, 200, 'failed login 3')
t.equal(obj.lockout, false, 'not locked out')

return client.postAsync('/check', { ip: TEST_IP, email: 'test2@example.com', action: ACCOUNT_LOGIN })
})
.spread(function(req, res, obj){
t.equal(res.statusCode, 200, 'returns a 200')
t.equal(obj.block, true, 'ip is now rate limited')
t.equal(obj.retryAfter, 2, 'rate limit retry amount')

return client.postAsync('/check', { ip: TEST_IP, email: 'test3@example.com', action: ACCOUNT_LOGIN })
})
// IP should be now blocked
.spread(function(req, res, obj){
t.equal(res.statusCode, 200, 'returns a 200')
t.equal(obj.block, true, 'ip is now rate limited')

// Delay ~2s for rate limit to go away
return Promise.delay(3010)
})
// IP should be now unblocked
.then(function(){
return client.postAsync('/check', { ip: TEST_IP, email: 'test5@example.com', action: ACCOUNT_LOGIN })
})
.spread(function(req, res, obj){
t.equal(res.statusCode, 200, 'returns a 200')
t.equal(obj.block, false, 'is not rate limited after RATE_LIMIT_INTERVAL_SECONDS')
t.end()
})
.catch(function(err){
t.fail(err)
t.end()
})
}
)

test(
'teardown',
function (t) {
testServer.stop()
t.equal(testServer.server.killed, true, 'test server has been killed')
t.end()
}
)

0 comments on commit 30f8a5d

Please sign in to comment.