adjust for postgres
This commit is contained in:
@ -16,8 +16,9 @@ ENV JWT_SECRET='streng_geheim'
|
|||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
apt update && \
|
apt update && \
|
||||||
apt install -y libmariadbclient-dev && \
|
apt install -y postgresql-client-common && \
|
||||||
pip3 install mariadb && \
|
pip3 install psycopg2 && \
|
||||||
|
pip3 install loguru && \
|
||||||
pip3 install dateparser && \
|
pip3 install dateparser && \
|
||||||
pip3 install connexion && \
|
pip3 install connexion && \
|
||||||
pip3 install connexion[swagger-ui] && \
|
pip3 install connexion[swagger-ui] && \
|
||||||
|
91
auth.py
91
auth.py
@ -6,6 +6,7 @@ import os
|
|||||||
import psycopg2
|
import psycopg2
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from pbkdf2 import crypt
|
from pbkdf2 import crypt
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
DB_USER = os.environ["DB_USER"]
|
DB_USER = os.environ["DB_USER"]
|
||||||
DB_PASS = os.environ["DB_PASS"]
|
DB_PASS = os.environ["DB_PASS"]
|
||||||
@ -28,8 +29,7 @@ class PasswordMismatchException(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
UserEntry = namedtuple('UserEntry', ['id', 'login', 'expiry', 'claims'])
|
UserEntry = namedtuple('UserEntry', ['id', 'login', 'pwhash', 'expiry', 'claims'])
|
||||||
|
|
||||||
|
|
||||||
JWT_PRIV_KEY = ""
|
JWT_PRIV_KEY = ""
|
||||||
try:
|
try:
|
||||||
@ -54,48 +54,46 @@ def getUserEntryFromDB(application: str, login: str):
|
|||||||
host = DB_HOST, database = DB_NAME)
|
host = DB_HOST, database = DB_NAME)
|
||||||
conn.autocommit = False
|
conn.autocommit = False
|
||||||
|
|
||||||
cur = conn.cursor(dictionary=True)
|
userObj = None
|
||||||
cur.execute("SELECT id, pwhash, expiry FROM user_application" +
|
with conn.cursor() as cur:
|
||||||
" WHERE application = ? AND login = ?",
|
cur.execute("SELECT id, pwhash, expiry FROM user_application_v" +
|
||||||
[application, login])
|
" WHERE application = %s AND login = %s",
|
||||||
resObj = cur.next()
|
(application, login))
|
||||||
print("DEBUG: getUserEntryFromDB: resObj: {}".format(resObj))
|
userObj = cur.fetchone()
|
||||||
if not resObj:
|
logger.debug("getUserEntryFromDB: userObj: {}".format(userObj))
|
||||||
raise NoUserException()
|
if not userObj:
|
||||||
invObj = cur.next()
|
raise NoUserException()
|
||||||
if invObj:
|
invObj = cur.fetchone()
|
||||||
raise ManyUsersException()
|
if invObj:
|
||||||
|
raise ManyUsersException()
|
||||||
|
|
||||||
userId = resObj["id"]
|
|
||||||
cur.execute("SELECT user, `key`, `value` FROM claims_for_user where user = ?",
|
|
||||||
[userId])
|
|
||||||
claims = {}
|
claims = {}
|
||||||
for claimObj in cur:
|
with conn.cursor() as cur:
|
||||||
print("DEBUG: getUserEntryFromDB: add claim {} -> {}".format(claimObj["key"], claimObj["value"]))
|
cur.execute('SELECT key, value FROM claims_for_user_v where "user" = %s and application = %s',
|
||||||
if claimObj["key"] in claims:
|
(userObj[0], application))
|
||||||
if isinstance(claims[claimObj["key"]], list):
|
for claimObj in cur:
|
||||||
claims[claimObj["key"]].append(claimObj["value"])
|
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])
|
||||||
else:
|
else:
|
||||||
claims[claimObj["key"]] = [ claims[claimObj["key"]] ]
|
claims[claimObj[0]] = claimObj[1]
|
||||||
claims[claimObj["key"]].append(claimObj["value"])
|
|
||||||
else:
|
|
||||||
claims[claimObj["key"]] = claimObj["value"]
|
|
||||||
|
|
||||||
userEntry = UserEntry(id=userId, login=login, expiry=resObj["expiry"], claims=claims)
|
userEntry = UserEntry(id=userObj[0], login=login, pwhash=userObj[1], expiry=userObj[2], claims=claims)
|
||||||
|
|
||||||
return userEntry, resObj["pwhash"]
|
return userEntry
|
||||||
except mariadb.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:
|
||||||
if cur:
|
|
||||||
cur.close()
|
|
||||||
if conn:
|
if conn:
|
||||||
conn.rollback()
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def getUserEntry(application, login, password):
|
def getUserEntry(application, login, password):
|
||||||
userEntry, pwhash = getUserEntryFromDB(application, login)
|
userEntry = getUserEntryFromDB(application, login)
|
||||||
if pwhash != crypt(password, pwhash):
|
if userEntry.pwhash != crypt(password, userEntry.pwhash):
|
||||||
raise PasswordMismatchException()
|
raise PasswordMismatchException()
|
||||||
return userEntry
|
return userEntry
|
||||||
|
|
||||||
@ -116,25 +114,26 @@ def generateToken(**args):
|
|||||||
"sub": str(userEntry.id),
|
"sub": str(userEntry.id),
|
||||||
"aud": application
|
"aud": application
|
||||||
}
|
}
|
||||||
|
logger.debug("claims: {}".format(userEntry.claims))
|
||||||
for claim in userEntry.claims.items():
|
for claim in userEntry.claims.items():
|
||||||
# print("DEBUG: generateToken: add claim {} -> {}".format(claim[0], claim[1]))
|
logger.debug("generateToken: add claim {}".format(claim))
|
||||||
payload[claim[0]] = claim[1]
|
payload[claim[0]] = claim[1]
|
||||||
|
|
||||||
return jwt.encode(payload, JWT_PRIV_KEY, algorithm='RS256')
|
return jwt.encode(payload, JWT_PRIV_KEY, algorithm='RS256')
|
||||||
except NoUserException:
|
except NoUserException:
|
||||||
print("ERROR: generateToken: no user found, login or application wrong")
|
logger.error("generateToken: no user found, login or application wrong")
|
||||||
raise werkzeug.exceptions.Unauthorized()
|
raise werkzeug.exceptions.Unauthorized()
|
||||||
except ManyUsersException:
|
except ManyUsersException:
|
||||||
print("ERROR: generateToken: too many users found")
|
logger.error("generateToken: too many users found")
|
||||||
raise werkzeug.exceptions.Unauthorized()
|
raise werkzeug.exceptions.Unauthorized()
|
||||||
except PasswordMismatchException:
|
except PasswordMismatchException:
|
||||||
print("ERROR: generateToken: wrong password")
|
logger.error("generateToken: wrong password")
|
||||||
raise werkzeug.exceptions.Unauthorized()
|
raise werkzeug.exceptions.Unauthorized()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print("ERROR: generateToken: application, login or password missing")
|
logger.error("generateToken: application, login or password missing")
|
||||||
raise werkzeug.exceptions.Unauthorized()
|
raise werkzeug.exceptions.Unauthorized()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("ERROR: generateToken: unspecific exception: {}".format(str(e)))
|
logger.error("generateToken: unspecific exception: {}".format(str(e)))
|
||||||
raise werkzeug.exceptions.Unauthorized()
|
raise werkzeug.exceptions.Unauthorized()
|
||||||
|
|
||||||
def generateTokenFromEnc(**args):
|
def generateTokenFromEnc(**args):
|
||||||
@ -144,3 +143,17 @@ def generateTokenFromEnc(**args):
|
|||||||
|
|
||||||
def getPubKey():
|
def getPubKey():
|
||||||
return JWT_PUB_KEY
|
return JWT_PUB_KEY
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
2
build.sh
2
build.sh
@ -4,5 +4,5 @@ IMAGE_NAME="registry.hottis.de/wolutator/authservice"
|
|||||||
VERSION=0.0.1
|
VERSION=0.0.1
|
||||||
|
|
||||||
docker build -t ${IMAGE_NAME}:${VERSION} .
|
docker build -t ${IMAGE_NAME}:${VERSION} .
|
||||||
docker push ${IMAGE_NAME}:${VERSION}
|
# docker push ${IMAGE_NAME}:${VERSION}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ create table claim_t (
|
|||||||
id integer primary key not null default nextval('claim_s'),
|
id integer primary key not null default nextval('claim_s'),
|
||||||
key varchar(64) not null,
|
key varchar(64) not null,
|
||||||
value varchar(64) not null,
|
value varchar(64) not null,
|
||||||
|
application integer not null references application(id),
|
||||||
unique (key, value)
|
unique (key, value)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -34,13 +35,16 @@ create table user_application_mapping_t (
|
|||||||
|
|
||||||
create or replace view claims_for_user_v as
|
create or replace view claims_for_user_v as
|
||||||
select u.id as "user",
|
select u.id as "user",
|
||||||
|
a.name as application,
|
||||||
c.key as key,
|
c.key as key,
|
||||||
c.value as value
|
c.value as value
|
||||||
from user_t u,
|
from user_t u,
|
||||||
claim_t c,
|
claim_t c,
|
||||||
user_claim_mapping_t m
|
user_claim_mapping_t m,
|
||||||
|
application_t a
|
||||||
where m.user = u.id and
|
where m.user = u.id and
|
||||||
m.claim = c.id;
|
m.claim = c.id and
|
||||||
|
a.id = c.application;
|
||||||
|
|
||||||
create or replace view user_application_v as
|
create or replace view user_application_v as
|
||||||
select u.login as login,
|
select u.login as login,
|
||||||
|
@ -42,7 +42,7 @@ paths:
|
|||||||
get:
|
get:
|
||||||
tags: [ "JWT" ]
|
tags: [ "JWT" ]
|
||||||
summary: Return secret string
|
summary: Return secret string
|
||||||
operationId: test.getSecret
|
operationId: auth.getSecret
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: secret response
|
description: secret response
|
||||||
@ -72,7 +72,7 @@ components:
|
|||||||
type: http
|
type: http
|
||||||
scheme: bearer
|
scheme: bearer
|
||||||
bearerFormat: JWT
|
bearerFormat: JWT
|
||||||
x-bearerInfoFunc: test.decodeToken
|
x-bearerInfoFunc: auth.decodeToken
|
||||||
schemas:
|
schemas:
|
||||||
User:
|
User:
|
||||||
description: Application/Login/Password tuple
|
description: Application/Login/Password tuple
|
||||||
|
24
test.py
24
test.py
@ -1,20 +1,8 @@
|
|||||||
from jose import JWTError, jwt
|
import connexion
|
||||||
import os
|
import logging
|
||||||
import werkzeug
|
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
JWT_SECRET = os.environ['JWT_SECRET']
|
app = connexion.App('authservice')
|
||||||
|
app.add_api('./openapi.yaml')
|
||||||
def decodeToken(token):
|
app.run(port=8080)
|
||||||
try:
|
|
||||||
return jwt.decode(token, JWT_SECRET)
|
|
||||||
except JWTError as e:
|
|
||||||
print("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)
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user