authservice/auth.py

160 lines
5.1 KiB
Python
Raw Normal View History

2021-01-25 21:52:52 +01:00
import time
import connexion
from jose import JWTError, jwt
2021-01-26 13:43:09 +01:00
import werkzeug
2021-01-25 21:52:52 +01:00
import os
2021-05-11 15:16:13 +02:00
import psycopg2
2021-01-26 13:43:09 +01:00
from collections import namedtuple
2021-01-26 21:27:17 +01:00
from pbkdf2 import crypt
2021-05-11 16:48:02 +02:00
from loguru import logger
2021-01-25 21:52:52 +01:00
DB_USER = os.environ["DB_USER"]
DB_PASS = os.environ["DB_PASS"]
DB_HOST = os.environ["DB_HOST"]
DB_NAME = os.environ["DB_NAME"]
2021-01-27 10:57:54 +01:00
JWT_ISSUER = os.environ["JWT_ISSUER"]
2021-05-06 16:37:32 +02:00
2021-01-27 10:57:54 +01:00
2021-01-25 21:52:52 +01:00
2021-01-26 21:27:17 +01:00
class NoUserException(Exception):
pass
class ManyUsersException(Exception):
pass
class PasswordMismatchException(Exception):
pass
2021-01-26 13:43:09 +01:00
2021-05-11 16:48:02 +02:00
UserEntry = namedtuple('UserEntry', ['id', 'login', 'pwhash', 'expiry', 'claims'])
2021-05-07 13:28:12 +02:00
2021-05-06 16:37:32 +02:00
JWT_PRIV_KEY = ""
2021-05-07 13:28:12 +02:00
try:
JWT_PRIV_KEY = os.environ["JWT_PRIV_KEY"]
except KeyError:
with open('/opt/app/config/authservice.key', 'r') as f:
JWT_PRIV_KEY = f.read()
2021-05-06 16:37:32 +02:00
JWT_PUB_KEY = ""
2021-05-07 13:28:12 +02:00
try:
JWT_PUB_KEY = os.environ["JWT_PUB_KEY"]
except KeyError:
with open('/opt/app/config/authservice.pub', 'r') as f:
JWT_PUB_KEY = f.read()
2021-01-26 13:43:09 +01:00
2021-01-26 22:06:39 +01:00
def getUserEntryFromDB(application: str, login: str):
2021-01-25 21:52:52 +01:00
conn = None
cur = None
try:
2021-05-11 15:16:13 +02:00
conn = psycopg2.connect(user = DB_USER, password = DB_PASS,
host = DB_HOST, database = DB_NAME)
2021-01-25 21:52:52 +01:00
conn.autocommit = False
2021-05-11 16:48:02 +02:00
userObj = None
with conn.cursor() as cur:
cur.execute("SELECT id, pwhash, expiry FROM user_application_v" +
" WHERE application = %s AND login = %s",
(application, login))
userObj = cur.fetchone()
logger.debug("getUserEntryFromDB: userObj: {}".format(userObj))
if not userObj:
raise NoUserException()
invObj = cur.fetchone()
if invObj:
raise ManyUsersException()
2021-01-26 13:43:09 +01:00
claims = {}
2021-05-11 16:48:02 +02:00
with conn.cursor() as cur:
cur.execute('SELECT key, value FROM claims_for_user_v where "user" = %s and application = %s',
(userObj[0], application))
for claimObj in cur:
logger.debug("getUserEntryFromDB: add claim {} -> {}".format(claimObj[0], claimObj[1]))
if claimObj[0] in claims:
if isinstance(claims[claimObj[0]], list):
claims[claimObj[0]].append(claimObj[1])
else:
claims[claimObj[0]] = [ claims[claimObj[0]] ]
claims[claimObj[0]].append(claimObj[1])
2021-01-26 13:43:09 +01:00
else:
2021-05-11 16:48:02 +02:00
claims[claimObj[0]] = claimObj[1]
2021-01-26 13:43:09 +01:00
2021-05-11 16:48:02 +02:00
userEntry = UserEntry(id=userObj[0], login=login, pwhash=userObj[1], expiry=userObj[2], claims=claims)
2021-01-25 21:52:52 +01:00
2021-05-11 16:48:02 +02:00
return userEntry
except psycopg2.Error as err:
2021-01-25 21:52:52 +01:00
raise Exception("Error when connecting to database: {}".format(err))
finally:
if conn:
conn.close()
2021-01-26 22:06:39 +01:00
def getUserEntry(application, login, password):
2021-05-11 16:48:02 +02:00
userEntry = getUserEntryFromDB(application, login)
if userEntry.pwhash != crypt(password, userEntry.pwhash):
2021-01-26 21:27:17 +01:00
raise PasswordMismatchException()
2021-01-26 22:06:39 +01:00
return userEntry
2021-01-25 21:52:52 +01:00
2021-01-26 13:43:09 +01:00
def generateToken(**args):
2021-01-25 21:52:52 +01:00
try:
2021-01-26 13:43:09 +01:00
body = args["body"]
2021-01-26 21:27:17 +01:00
application = body["application"]
2021-01-26 13:43:09 +01:00
login = body["login"]
2021-01-26 22:06:39 +01:00
password = body["password"]
2021-01-26 21:27:17 +01:00
2021-01-26 22:06:39 +01:00
userEntry = getUserEntry(application, login, password)
2021-01-26 21:27:17 +01:00
2021-01-26 13:43:09 +01:00
timestamp = int(time.time())
payload = {
2021-01-27 10:57:54 +01:00
"iss": JWT_ISSUER,
2021-01-26 13:43:09 +01:00
"iat": int(timestamp),
"exp": int(timestamp + userEntry.expiry),
2021-05-07 14:40:40 +02:00
"sub": str(userEntry.id),
"aud": application
2021-01-26 13:43:09 +01:00
}
2021-05-11 16:48:02 +02:00
logger.debug("claims: {}".format(userEntry.claims))
2021-01-26 13:43:09 +01:00
for claim in userEntry.claims.items():
2021-05-11 16:48:02 +02:00
logger.debug("generateToken: add claim {}".format(claim))
2021-01-27 13:31:34 +01:00
payload[claim[0]] = claim[1]
2021-01-26 13:43:09 +01:00
2021-05-06 16:37:32 +02:00
return jwt.encode(payload, JWT_PRIV_KEY, algorithm='RS256')
2021-01-26 13:43:09 +01:00
except NoUserException:
2021-05-11 16:48:02 +02:00
logger.error("generateToken: no user found, login or application wrong")
2021-01-26 13:43:09 +01:00
raise werkzeug.exceptions.Unauthorized()
except ManyUsersException:
2021-05-11 16:48:02 +02:00
logger.error("generateToken: too many users found")
2021-01-26 13:43:09 +01:00
raise werkzeug.exceptions.Unauthorized()
2021-01-26 21:27:17 +01:00
except PasswordMismatchException:
2021-05-11 16:48:02 +02:00
logger.error("generateToken: wrong password")
2021-01-26 21:27:17 +01:00
raise werkzeug.exceptions.Unauthorized()
2021-01-26 13:43:09 +01:00
except KeyError:
2021-05-11 16:48:02 +02:00
logger.error("generateToken: application, login or password missing")
2021-01-26 13:43:09 +01:00
raise werkzeug.exceptions.Unauthorized()
except Exception as e:
2021-05-11 16:48:02 +02:00
logger.error("generateToken: unspecific exception: {}".format(str(e)))
2021-01-26 13:43:09 +01:00
raise werkzeug.exceptions.Unauthorized()
2021-05-06 16:37:32 +02:00
2021-05-07 13:28:12 +02:00
def generateTokenFromEnc(**args):
cryptContent = args["body"]
2021-05-07 14:06:18 +02:00
raise werkzeug.exceptions.NotImplemented("Stay tuned, will be added soon")
2021-05-07 13:28:12 +02:00
return str(cryptContent)
2021-05-07 12:15:30 +02:00
2021-05-06 16:37:32 +02:00
def getPubKey():
return JWT_PUB_KEY
2021-05-11 16:48:02 +02:00
def decodeToken(token):
try:
return jwt.decode(token, JWT_PUB_KEY, audience="test")
except JWTError as e:
logger.error("decodeToken: {}".format(e))
raise werkzeug.exceptions.Unauthorized()
def getSecret(user, token_info):
return '''
You are user_id {user} and the secret is 'wbevuec'.
Decoded token claims: {token_info}.
'''.format(user=user, token_info=token_info)