add login rate limiter
This commit is contained in:
		
							parent
							
								
									8a481a1be0
								
							
						
					
					
						commit
						b77b33e790
					
				
					 3 changed files with 66 additions and 11 deletions
				
			
		|  | @ -1,8 +1,9 @@ | ||||||
| const basicAuth = require("express-basic-auth") | const basicAuth = require("express-basic-auth"); | ||||||
| const passwordHash = require("./password-hash"); | const passwordHash = require("./password-hash"); | ||||||
| const { R } = require("redbean-node"); | const { R } = require("redbean-node"); | ||||||
| const { setting } = require("./util-server"); | const { setting } = require("./util-server"); | ||||||
| const { debug } = require("../src/util"); | const { debug } = require("../src/util"); | ||||||
|  | const { loginRateLimiter } = require("./rate-limiter"); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * |  * | ||||||
|  | @ -13,7 +14,7 @@ const { debug } = require("../src/util"); | ||||||
| exports.login = async function (username, password) { | exports.login = async function (username, password) { | ||||||
|     let user = await R.findOne("user", " username = ? AND active = 1 ", [ |     let user = await R.findOne("user", " username = ? AND active = 1 ", [ | ||||||
|         username, |         username, | ||||||
|     ]) |     ]); | ||||||
| 
 | 
 | ||||||
|     if (user && passwordHash.verify(password, user.password)) { |     if (user && passwordHash.verify(password, user.password)) { | ||||||
|         // Upgrade the hash to bcrypt
 |         // Upgrade the hash to bcrypt
 | ||||||
|  | @ -27,21 +28,30 @@ exports.login = async function (username, password) { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return null; |     return null; | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function myAuthorizer(username, password, callback) { | function myAuthorizer(username, password, callback) { | ||||||
| 
 |  | ||||||
|     setting("disableAuth").then((result) => { |     setting("disableAuth").then((result) => { | ||||||
| 
 |  | ||||||
|         if (result) { |         if (result) { | ||||||
|             callback(null, true) |             callback(null, true); | ||||||
|         } else { |         } else { | ||||||
|  |             // Login Rate Limit
 | ||||||
|  |             loginRateLimiter.pass(null, 0).then((pass) => { | ||||||
|  |                 if (pass) { | ||||||
|                     exports.login(username, password).then((user) => { |                     exports.login(username, password).then((user) => { | ||||||
|                 callback(null, user != null) |                         callback(null, user != null); | ||||||
|             }) |  | ||||||
|         } |  | ||||||
|     }) |  | ||||||
| 
 | 
 | ||||||
|  |                         if (user == null) { | ||||||
|  |                             loginRateLimiter.removeTokens(1); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                 } else { | ||||||
|  |                     callback(null, false); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| exports.basicAuth = basicAuth({ | exports.basicAuth = basicAuth({ | ||||||
|  |  | ||||||
							
								
								
									
										39
									
								
								server/rate-limiter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								server/rate-limiter.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | ||||||
|  | const { RateLimiter } = require("limiter"); | ||||||
|  | const { debug } = require("../src/util"); | ||||||
|  | 
 | ||||||
|  | class KumaRateLimiter { | ||||||
|  |     constructor(config) { | ||||||
|  |         this.errorMessage = config.errorMessage; | ||||||
|  |         this.rateLimiter = new RateLimiter(config); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async pass(callback, num = 1) { | ||||||
|  |         const remainingRequests = await this.removeTokens(num); | ||||||
|  |         debug("Rate Limit (remainingRequests):" + remainingRequests); | ||||||
|  |         if (remainingRequests < 0) { | ||||||
|  |             if (callback) { | ||||||
|  |                 callback({ | ||||||
|  |                     ok: false, | ||||||
|  |                     msg: this.errorMessage, | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async removeTokens(num = 1) { | ||||||
|  |         return await this.rateLimiter.removeTokens(num); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const loginRateLimiter = new KumaRateLimiter({ | ||||||
|  |     tokensPerInterval: 20, | ||||||
|  |     interval: "minute", | ||||||
|  |     fireImmediately: true, | ||||||
|  |     errorMessage: "Too frequently, try again later." | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | module.exports = { | ||||||
|  |     loginRateLimiter | ||||||
|  | }; | ||||||
|  | @ -52,6 +52,7 @@ const Database = require("./database"); | ||||||
| 
 | 
 | ||||||
| debug("Importing Background Jobs"); | debug("Importing Background Jobs"); | ||||||
| const { initBackgroundJobs } = require("./jobs"); | const { initBackgroundJobs } = require("./jobs"); | ||||||
|  | const { loginRateLimiter } = require("./rate-limiter"); | ||||||
| 
 | 
 | ||||||
| const { basicAuth } = require("./auth"); | const { basicAuth } = require("./auth"); | ||||||
| const { login } = require("./auth"); | const { login } = require("./auth"); | ||||||
|  | @ -281,6 +282,11 @@ exports.entryPage = "dashboard"; | ||||||
|         socket.on("login", async (data, callback) => { |         socket.on("login", async (data, callback) => { | ||||||
|             console.log("Login"); |             console.log("Login"); | ||||||
| 
 | 
 | ||||||
|  |             // Login Rate Limit
 | ||||||
|  |             if (! await loginRateLimiter.pass(callback)) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             let user = await login(data.username, data.password); |             let user = await login(data.username, data.password); | ||||||
| 
 | 
 | ||||||
|             if (user) { |             if (user) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue