Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
d521e146cf
|
|||
0d698568d4
|
|||
3541226964
|
|||
216e1c0684
|
|||
f8afd95a82
|
|||
e29ce48971
|
|||
7163db9ce9
|
|||
629a85fc3e
|
20
asadduser.py
20
asadduser.py
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import mariadb
|
||||
import psycopg2
|
||||
from pbkdf2 import crypt
|
||||
import argparse
|
||||
import os
|
||||
@ -23,30 +23,26 @@ password = args.password
|
||||
application = args.application
|
||||
|
||||
|
||||
DB_USER = os.environ["DB_USER"]
|
||||
DB_PASS = os.environ["DB_PASS"]
|
||||
DB_HOST = os.environ["DB_HOST"]
|
||||
DB_NAME = os.environ["DB_NAME"]
|
||||
DB_NAME = "authservice"
|
||||
|
||||
pwhash = crypt(password, iterations=100000)
|
||||
|
||||
conn = None
|
||||
cur = None
|
||||
try:
|
||||
conn = mariadb.connect(user = DB_USER, password = DB_PASS,
|
||||
host = DB_HOST, database = DB_NAME)
|
||||
conn = psycopg2.connect(database = DB_NAME)
|
||||
conn.autocommit = False
|
||||
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
INSERT INTO users (login, pwhash)
|
||||
VALUES(?, ?)
|
||||
INSERT INTO user_t (login, pwhash)
|
||||
VALUES(%s, %s)
|
||||
""", [user, pwhash])
|
||||
cur.execute("""
|
||||
INSERT INTO user_applications_mapping (application, user)
|
||||
INSERT INTO user_application_mapping_t (application,"user")
|
||||
VALUES(
|
||||
(SELECT id FROM applications WHERE name = ?),
|
||||
(SELECT id FROM users WHERE login = ?)
|
||||
(SELECT id FROM application_t WHERE name = %s),
|
||||
(SELECT id FROM user_t WHERE login = %s)
|
||||
)
|
||||
""", [application, user])
|
||||
conn.commit()
|
||||
|
90
auth.py
90
auth.py
@ -12,6 +12,7 @@ from loguru import logger
|
||||
import configparser
|
||||
import random
|
||||
import string
|
||||
from flask import request
|
||||
|
||||
|
||||
DB_USER = ""
|
||||
@ -38,6 +39,9 @@ except KeyError:
|
||||
class NoUserException(Exception):
|
||||
pass
|
||||
|
||||
class RefreshTokenExpiredException(Exception):
|
||||
pass
|
||||
|
||||
class NoTokenException(Exception):
|
||||
pass
|
||||
|
||||
@ -120,7 +124,7 @@ def getUserEntryFromDB(application: str, login: str):
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
def getRefreshTokenFromDB(application, login):
|
||||
def getRefreshTokenFromDB(application, login, httpClientIp):
|
||||
conn = None
|
||||
cur = None
|
||||
try:
|
||||
@ -145,8 +149,8 @@ def getRefreshTokenFromDB(application, login):
|
||||
|
||||
with conn.cursor() as cur:
|
||||
salt = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(64))
|
||||
cur.execute('INSERT INTO token_t ("user", salt) VALUES (%s, %s) RETURNING id',
|
||||
(userObj[0], salt))
|
||||
cur.execute('INSERT INTO token_t ("user", salt, expiry, client_ip) VALUES (%s, %s, %s, %s) RETURNING id',
|
||||
(userObj[0], salt, userObj[1], httpClientIp))
|
||||
tokenObj = cur.fetchone()
|
||||
logger.debug("tokenObj: {}".format(tokenObj))
|
||||
if not tokenObj:
|
||||
@ -192,9 +196,14 @@ def generateToken(func, **args):
|
||||
else:
|
||||
raise KeyError("Neither application, login and password nor encAleTuple given")
|
||||
|
||||
logger.debug(f"Tuple: {application} {login} {password}")
|
||||
if request.headers.getlist("X-Forwarded-For"):
|
||||
httpClientIp = request.headers.getlist("X-Forwarded-For")[0]
|
||||
else:
|
||||
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:
|
||||
logger.error("no token created")
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
@ -218,7 +227,7 @@ def generateToken(func, **args):
|
||||
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)
|
||||
|
||||
timestamp = int(time.time())
|
||||
@ -236,8 +245,8 @@ def _makeSimpleToken(application, login, password, refresh=False):
|
||||
|
||||
return jwt.encode(payload, JWT_PRIV_KEY, algorithm='RS256')
|
||||
|
||||
def _makeRefreshToken(application, login, password):
|
||||
refreshTokenEntry = getRefreshTokenFromDB(application, login)
|
||||
def _makeRefreshToken(application, login, password, httpClientIp):
|
||||
refreshTokenEntry = getRefreshTokenFromDB(application, login, httpClientIp)
|
||||
|
||||
timestamp = int(time.time())
|
||||
payload = {
|
||||
@ -252,17 +261,17 @@ def _makeRefreshToken(application, login, password):
|
||||
|
||||
return jwt.encode(payload, JWT_PRIV_KEY, algorithm='RS256')
|
||||
|
||||
def _makeRefreshableTokens(application, login, password):
|
||||
authToken = _makeSimpleToken(application, login, password)
|
||||
refreshToken = _makeRefreshToken(application, login, password)
|
||||
def _makeRefreshableTokens(application, login, password, httpClientIp):
|
||||
authToken = _makeSimpleToken(application, login, password, httpClientIp)
|
||||
refreshToken = _makeRefreshToken(application, login, password, httpClientIp)
|
||||
return {
|
||||
"authToken": authToken,
|
||||
"refreshToken": refreshToken
|
||||
}
|
||||
|
||||
def generateSimpleToken(**args):
|
||||
return generateToken(_makeSimpleToken, **args)
|
||||
|
||||
return generateToken(_makeSimpleToken, **args)
|
||||
def generateRefreshableTokens(**args):
|
||||
return generateToken(_makeRefreshableTokens, **args)
|
||||
|
||||
@ -283,34 +292,44 @@ def testToken(user, token_info):
|
||||
}
|
||||
|
||||
|
||||
def checkAndInvalidateRefreshToken(login, xid, xal):
|
||||
conn = None
|
||||
cur = None
|
||||
def checkAndInvalidateRefreshToken(login, xid, xal, httpClientIp):
|
||||
try:
|
||||
validTokenFound = False
|
||||
|
||||
conn = psycopg2.connect(user = DB_USER, password = DB_PASS,
|
||||
host = DB_HOST, database = DB_NAME)
|
||||
conn.autocommit = False
|
||||
|
||||
with conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute('SELECT t.id FROM token_t t, user_t u' +
|
||||
' WHERE t.id = %s AND ' +
|
||||
cur.execute('SELECT t.id, t.client_ip FROM token_t t, user_t u' +
|
||||
' WHERE t.valid = true AND ' +
|
||||
' t.id = %s AND ' +
|
||||
' t.salt = %s AND ' +
|
||||
' t."user" = u.id AND ' +
|
||||
' u.login = %s AND ' +
|
||||
' t.valid = true',
|
||||
' u.login = %s',
|
||||
(xid, xal, login))
|
||||
tokenObj = cur.fetchone()
|
||||
logger.debug("tokenObj: {}".format(tokenObj))
|
||||
if not tokenObj:
|
||||
raise NoValidTokenException()
|
||||
raise NoTokenException()
|
||||
invObj = cur.fetchone()
|
||||
if invObj:
|
||||
raise ManyTokensException()
|
||||
|
||||
with conn.cursor() as cur:
|
||||
cur.execute('UPDATE token_t SET valid = false WHERE id = %s',
|
||||
[ xid ])
|
||||
if (tokenObj[1] == httpClientIp):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute('UPDATE token_t SET used = used + 1 WHERE id = %s',
|
||||
[ 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:
|
||||
raise Exception("Error when connecting to database: {}".format(err))
|
||||
finally:
|
||||
@ -324,10 +343,18 @@ def refreshTokens(**args):
|
||||
refreshTokenObj = jwt.decode(refreshToken, JWT_PUB_KEY)
|
||||
logger.info(str(refreshTokenObj))
|
||||
|
||||
checkAndInvalidateRefreshToken(refreshTokenObj["sub"], refreshTokenObj["xid"], refreshTokenObj["xal"])
|
||||
if request.headers.getlist("X-Forwarded-For"):
|
||||
httpClientIp = request.headers.getlist("X-Forwarded-For")[0]
|
||||
else:
|
||||
httpClientIp = request.remote_addr
|
||||
|
||||
authToken = _makeSimpleToken(refreshTokenObj["xap"], refreshTokenObj["sub"], "", refresh=True)
|
||||
refreshToken = _makeRefreshToken(refreshTokenObj["xap"], refreshTokenObj["sub"], "")
|
||||
if refreshTokenObj["exp"] < int(time.time()):
|
||||
raise RefreshTokenExpiredException()
|
||||
|
||||
checkAndInvalidateRefreshToken(refreshTokenObj["sub"], refreshTokenObj["xid"], refreshTokenObj["xal"], httpClientIp)
|
||||
|
||||
authToken = _makeSimpleToken(refreshTokenObj["xap"], refreshTokenObj["sub"], "", httpClientIp, refresh=True)
|
||||
refreshToken = _makeRefreshToken(refreshTokenObj["xap"], refreshTokenObj["sub"], "", httpClientIp)
|
||||
return {
|
||||
"authToken": authToken,
|
||||
"refreshToken": refreshToken
|
||||
@ -335,8 +362,11 @@ def refreshTokens(**args):
|
||||
except JWTError as e:
|
||||
logger.error("jwt.decode failed: {}".format(e))
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
except RefreshTokenExpiredException:
|
||||
logger.error("refresh token expired")
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
except NoTokenException:
|
||||
logger.error("no token created")
|
||||
logger.error("no token created/found")
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
except NoValidTokenException:
|
||||
logger.error("no valid token found")
|
||||
@ -356,8 +386,8 @@ def refreshTokens(**args):
|
||||
except KeyError:
|
||||
logger.error("application, login or password missing")
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
#except Exception as e:
|
||||
# logger.error("unspecific exception: {}".format(str(e)))
|
||||
# raise werkzeug.exceptions.Unauthorized()
|
||||
except Exception as e:
|
||||
logger.error("unspecific exception: {}".format(str(e)))
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user