consider client ip
This commit is contained in:
56
auth.py
56
auth.py
@ -12,6 +12,7 @@ from loguru import logger
|
|||||||
import configparser
|
import configparser
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
|
||||||
DB_USER = ""
|
DB_USER = ""
|
||||||
@ -123,7 +124,7 @@ def getUserEntryFromDB(application: str, login: str):
|
|||||||
if conn:
|
if conn:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def getRefreshTokenFromDB(application, login):
|
def getRefreshTokenFromDB(application, login, httpClientIp):
|
||||||
conn = None
|
conn = None
|
||||||
cur = None
|
cur = None
|
||||||
try:
|
try:
|
||||||
@ -148,8 +149,8 @@ def getRefreshTokenFromDB(application, login):
|
|||||||
|
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
salt = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(64))
|
salt = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(64))
|
||||||
cur.execute('INSERT INTO token_t ("user", salt, expiry) VALUES (%s, %s, %s) RETURNING id',
|
cur.execute('INSERT INTO token_t ("user", salt, expiry, client_ip) VALUES (%s, %s, %s, %s) RETURNING id',
|
||||||
(userObj[0], salt, userObj[1]))
|
(userObj[0], salt, userObj[1], httpClientIp))
|
||||||
tokenObj = cur.fetchone()
|
tokenObj = cur.fetchone()
|
||||||
logger.debug("tokenObj: {}".format(tokenObj))
|
logger.debug("tokenObj: {}".format(tokenObj))
|
||||||
if not tokenObj:
|
if not tokenObj:
|
||||||
@ -195,9 +196,11 @@ def generateToken(func, **args):
|
|||||||
else:
|
else:
|
||||||
raise KeyError("Neither application, login and password nor encAleTuple given")
|
raise KeyError("Neither application, login and password nor encAleTuple given")
|
||||||
|
|
||||||
logger.debug(f"Tuple: {application} {login} {password}")
|
httpClientIp = request.remote_addr
|
||||||
|
|
||||||
return func(application, login, password)
|
logger.debug(f"Tuple: {application} {login} {password} {httpClientIp}")
|
||||||
|
|
||||||
|
return func(application, login, password, httpClientIp)
|
||||||
except NoTokenException:
|
except NoTokenException:
|
||||||
logger.error("no token created")
|
logger.error("no token created")
|
||||||
raise werkzeug.exceptions.Unauthorized()
|
raise werkzeug.exceptions.Unauthorized()
|
||||||
@ -221,7 +224,7 @@ def generateToken(func, **args):
|
|||||||
raise werkzeug.exceptions.Unauthorized()
|
raise werkzeug.exceptions.Unauthorized()
|
||||||
|
|
||||||
|
|
||||||
def _makeSimpleToken(application, login, password, refresh=False):
|
def _makeSimpleToken(application, login, password, httpClientIp, refresh=False):
|
||||||
userEntry = getUserEntry(application, login, password) if not refresh else getUserEntryFromDB(application, login)
|
userEntry = getUserEntry(application, login, password) if not refresh else getUserEntryFromDB(application, login)
|
||||||
|
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
@ -239,8 +242,8 @@ def _makeSimpleToken(application, login, password, refresh=False):
|
|||||||
|
|
||||||
return jwt.encode(payload, JWT_PRIV_KEY, algorithm='RS256')
|
return jwt.encode(payload, JWT_PRIV_KEY, algorithm='RS256')
|
||||||
|
|
||||||
def _makeRefreshToken(application, login, password):
|
def _makeRefreshToken(application, login, password, httpClientIp):
|
||||||
refreshTokenEntry = getRefreshTokenFromDB(application, login)
|
refreshTokenEntry = getRefreshTokenFromDB(application, login, httpClientIp)
|
||||||
|
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
payload = {
|
payload = {
|
||||||
@ -255,17 +258,17 @@ def _makeRefreshToken(application, login, password):
|
|||||||
|
|
||||||
return jwt.encode(payload, JWT_PRIV_KEY, algorithm='RS256')
|
return jwt.encode(payload, JWT_PRIV_KEY, algorithm='RS256')
|
||||||
|
|
||||||
def _makeRefreshableTokens(application, login, password):
|
def _makeRefreshableTokens(application, login, password, httpClientIp):
|
||||||
authToken = _makeSimpleToken(application, login, password)
|
authToken = _makeSimpleToken(application, login, password, httpClientIp)
|
||||||
refreshToken = _makeRefreshToken(application, login, password)
|
refreshToken = _makeRefreshToken(application, login, password, httpClientIp)
|
||||||
return {
|
return {
|
||||||
"authToken": authToken,
|
"authToken": authToken,
|
||||||
"refreshToken": refreshToken
|
"refreshToken": refreshToken
|
||||||
}
|
}
|
||||||
|
|
||||||
def generateSimpleToken(**args):
|
def generateSimpleToken(**args):
|
||||||
return generateToken(_makeSimpleToken, **args)
|
|
||||||
|
|
||||||
|
return generateToken(_makeSimpleToken, **args)
|
||||||
def generateRefreshableTokens(**args):
|
def generateRefreshableTokens(**args):
|
||||||
return generateToken(_makeRefreshableTokens, **args)
|
return generateToken(_makeRefreshableTokens, **args)
|
||||||
|
|
||||||
@ -286,18 +289,19 @@ def testToken(user, token_info):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def checkAndInvalidateRefreshToken(login, xid, xal):
|
def checkAndInvalidateRefreshToken(login, xid, xal, httpClientIp):
|
||||||
conn = None
|
|
||||||
cur = None
|
|
||||||
try:
|
try:
|
||||||
|
validTokenFound = False
|
||||||
|
|
||||||
conn = psycopg2.connect(user = DB_USER, password = DB_PASS,
|
conn = psycopg2.connect(user = DB_USER, password = DB_PASS,
|
||||||
host = DB_HOST, database = DB_NAME)
|
host = DB_HOST, database = DB_NAME)
|
||||||
conn.autocommit = False
|
conn.autocommit = False
|
||||||
|
|
||||||
with conn:
|
with conn:
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute('SELECT t.id FROM token_t t, user_t u' +
|
cur.execute('SELECT t.id, t.client_ip FROM token_t t, user_t u' +
|
||||||
' WHERE t.id = %s AND ' +
|
' WHERE t.valid = true AND ' +
|
||||||
|
' t.id = %s AND ' +
|
||||||
' t.salt = %s AND ' +
|
' t.salt = %s AND ' +
|
||||||
' t."user" = u.id AND ' +
|
' t."user" = u.id AND ' +
|
||||||
' u.login = %s',
|
' u.login = %s',
|
||||||
@ -310,9 +314,19 @@ def checkAndInvalidateRefreshToken(login, xid, xal):
|
|||||||
if invObj:
|
if invObj:
|
||||||
raise ManyTokensException()
|
raise ManyTokensException()
|
||||||
|
|
||||||
|
if (tokenObj[1] == httpClientIp):
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute('UPDATE token_t SET used = used + 1 WHERE id = %s',
|
cur.execute('UPDATE token_t SET used = used + 1 WHERE id = %s',
|
||||||
[ xid ])
|
[ xid ])
|
||||||
|
validTokenFound = True
|
||||||
|
else:
|
||||||
|
logger.warning(f"Client IP in token {tokenObj[1]} and current one {httpClientIp} does not match")
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute('UPDATE token_t SET valid = false WHERE id = %s',
|
||||||
|
[ xid ])
|
||||||
|
if (not validTokenFound):
|
||||||
|
raise NoValidTokenException()
|
||||||
|
|
||||||
except psycopg2.Error as err:
|
except psycopg2.Error as err:
|
||||||
raise Exception("Error when connecting to database: {}".format(err))
|
raise Exception("Error when connecting to database: {}".format(err))
|
||||||
finally:
|
finally:
|
||||||
@ -326,13 +340,15 @@ def refreshTokens(**args):
|
|||||||
refreshTokenObj = jwt.decode(refreshToken, JWT_PUB_KEY)
|
refreshTokenObj = jwt.decode(refreshToken, JWT_PUB_KEY)
|
||||||
logger.info(str(refreshTokenObj))
|
logger.info(str(refreshTokenObj))
|
||||||
|
|
||||||
|
httpClientIp = request.remote_addr
|
||||||
|
|
||||||
if refreshTokenObj["exp"] < int(time.time()):
|
if refreshTokenObj["exp"] < int(time.time()):
|
||||||
raise RefreshTokenExpiredException()
|
raise RefreshTokenExpiredException()
|
||||||
|
|
||||||
checkAndInvalidateRefreshToken(refreshTokenObj["sub"], refreshTokenObj["xid"], refreshTokenObj["xal"])
|
checkAndInvalidateRefreshToken(refreshTokenObj["sub"], refreshTokenObj["xid"], refreshTokenObj["xal"], httpClientIp)
|
||||||
|
|
||||||
authToken = _makeSimpleToken(refreshTokenObj["xap"], refreshTokenObj["sub"], "", refresh=True)
|
authToken = _makeSimpleToken(refreshTokenObj["xap"], refreshTokenObj["sub"], "", httpClientIp, refresh=True)
|
||||||
refreshToken = _makeRefreshToken(refreshTokenObj["xap"], refreshTokenObj["sub"], "")
|
refreshToken = _makeRefreshToken(refreshTokenObj["xap"], refreshTokenObj["sub"], "", httpClientIp)
|
||||||
return {
|
return {
|
||||||
"authToken": authToken,
|
"authToken": authToken,
|
||||||
"refreshToken": refreshToken
|
"refreshToken": refreshToken
|
||||||
|
Reference in New Issue
Block a user