From ca9e0b81d32654ca2499fd35eb1a46ac5fcc6394 Mon Sep 17 00:00:00 2001 From: Wolfgang Hottgenroth Date: Tue, 26 Jan 2021 21:27:17 +0100 Subject: [PATCH] application and pwhash --- auth.py | 54 ++++++++++++++++++++++++++++++---------------- initial-schema.sql | 30 ++++++++++++++++++++++---- openapi.yaml | 4 +++- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/auth.py b/auth.py index d2c44e1..fd790b0 100755 --- a/auth.py +++ b/auth.py @@ -5,7 +5,7 @@ import werkzeug import os import mariadb from collections import namedtuple - +from pbkdf2 import crypt DB_USER = os.environ["DB_USER"] DB_PASS = os.environ["DB_PASS"] @@ -13,14 +13,21 @@ DB_HOST = os.environ["DB_HOST"] DB_NAME = os.environ["DB_NAME"] -class NoUserException(Exception): pass -class ManyUsersException(Exception): pass +class NoUserException(Exception): + pass -UserEntry = namedtuple('UserEntry', ['id', 'login', 'issuer', 'secret', 'expiry', 'claims']) +class ManyUsersException(Exception): + pass + +class PasswordMismatchException(Exception): + pass + + +UserEntry = namedtuple('UserEntry', ['id', 'login', 'pwhash', 'issuer', 'secret', 'expiry', 'claims']) -def getUserEntryFromDB(login: str, password: str) -> UserEntry: +def getUserEntryFromDB(application: str, login: str) -> UserEntry: conn = None cur = None try: @@ -29,9 +36,9 @@ def getUserEntryFromDB(login: str, password: str) -> UserEntry: conn.autocommit = False cur = conn.cursor(dictionary=True) - # print("DEBUG: getUserEntryFromDB: login: <{}>, password: <{}>".format(login, password)) - cur.execute("SELECT id, issuer, secret, expiry FROM user_and_issuer WHERE login = ? AND password = ?", - [login, password]) + cur.execute("SELECT id, password, issuer, secret, expiry FROM user_application_and_issuer " + + " WHERE application = ? AND login = ?", + [application, login]) resObj = cur.next() print("DEBUG: getUserEntryFromDB: resObj: {}".format(resObj)) if not resObj: @@ -55,9 +62,9 @@ def getUserEntryFromDB(login: str, password: str) -> UserEntry: else: claims[claimObj["key"]] = claimObj["value"] - userEntry = UserEntry(id=userId, login=login, secret=resObj["secret"], - issuer=resObj["issuer"], expiry=resObj["expiry"], - claims=claims) + userEntry = UserEntry(id=userId, login=login, pwhash=resObj["password"], + secret=resObj["secret"], issuer=resObj["issuer"], + expiry=resObj["expiry"], claims=claims) return userEntry except mariadb.Error as err: @@ -69,17 +76,23 @@ def getUserEntryFromDB(login: str, password: str) -> UserEntry: conn.rollback() conn.close() - -def getUserEntry(login: str, password: str) -> UserEntry: - return getUserEntryFromDB(login, password) - +def checkPassword(inputPassword, passwordHash) -> bool: + print("DEBUG, checkPassword: {} {}".format(inputPassword, passwordHash)) + if passwordHash != crypt(inputPassword, passwordHash, 100000): + raise PasswordMismatchException() + return True def generateToken(**args): try: body = args["body"] + application = body["application"] login = body["login"] - password = body["password"] - userEntry = getUserEntryFromDB(login, password) + inputPassword = body["password"] + + userEntry = getUserEntryFromDB(application, login) + + if inputPassword != crypt(inputPassword, userEntry.pwhash, 100000): + raise PasswordMismatchException() timestamp = int(time.time()) payload = { @@ -94,13 +107,16 @@ def generateToken(**args): return jwt.encode(payload, userEntry.secret) except NoUserException: - print("ERROR: generateToken: no user found, login or password wrong") + print("ERROR: generateToken: no user found, login or application wrong") raise werkzeug.exceptions.Unauthorized() except ManyUsersException: print("ERROR: generateToken: too many users found") raise werkzeug.exceptions.Unauthorized() + except PasswordMismatchException: + print("ERROR: generateToken: wrong password") + raise werkzeug.exceptions.Unauthorized() except KeyError: - print("ERROR: generateToken: login or password missing") + print("ERROR: generateToken: application, login or password missing") raise werkzeug.exceptions.Unauthorized() except Exception as e: print("ERROR: generateToken: unspecific exception: {}".format(str(e))) diff --git a/initial-schema.sql b/initial-schema.sql index ee4576a..4e22f66 100644 --- a/initial-schema.sql +++ b/initial-schema.sql @@ -8,7 +8,14 @@ CREATE TABLE `issuers` ( ) ENGINE=InnoDB; ALTER TABLE `issuers` - MODIFY COLUMN `max_expiry` int(10) unsigned NOT NULL; + MODIFY COLUMN `max_expiry` int(10) unsigned NOT NULL; + +CREATE TABLE `applications` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(128) NOT NULL, + CONSTRAINT PRIMARY KEY (`id`), + CONSTRAINT UNIQUE KEY `uk_applications_name` (`name`) +) ENGINE=InnoDB; CREATE TABLE `users` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, @@ -58,6 +65,16 @@ CREATE TABLE `user_claims_mapping` ( REFERENCES `claims`(`id`) ) ENGINE=InnoDB; +CREATE TABLE `user_applications_mapping` ( + `user` int(10) unsigned NOT NULL, + `application` int(10) unsigned NOT NULL, + CONSTRAINT UNIQUE KEY `uk_user_applications_mapping` (`user`, `application` ), + CONSTRAINT FOREIGN KEY `fk_user_applications_mapping_user` (`user`) + REFERENCES `users`(`id`), + CONSTRAINT FOREIGN KEY `fk_user_applications_mapping_application` (`application`) + REFERENCES `applications`(`id`) +) ENGINE=InnoDB; + CREATE OR REPLACE VIEW claims_for_user AS SELECT u.id AS user, c.`key` AS `key`, @@ -68,14 +85,19 @@ CREATE OR REPLACE VIEW claims_for_user AS WHERE m.user = u.id AND m.claim = c.id; -CREATE OR REPLACE VIEW user_and_issuer AS +CREATE OR REPLACE VIEW user_application_and_issuer AS SELECT u.login AS login, u.password AS password, u.id AS id, + a.name as application, i.name AS issuer, i.secret AS secret, least(u.expiry, i.max_expiry) AS expiry FROM users u, - issuers i - WHERE u.issuer = i.id; + issuers i, + applications a, + user_applications_mapping m + WHERE u.issuer = i.id AND + u.id = m.user AND + a.id = m.application; diff --git a/openapi.yaml b/openapi.yaml index 1ec7e63..b547750 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -45,9 +45,11 @@ components: x-bearerInfoFunc: test.decodeToken schemas: User: - description: Login/Password tuple + description: Application/Login/Password tuple type: object properties: + application: + type: string login: type: string password: