4 Commits
0.0.1 ... 0.0.3

Author SHA1 Message Date
ca17c556d6 disable ui 2021-01-26 22:36:11 +01:00
ef1b8ddf30 add module to dockerfile 2021-01-26 22:11:08 +01:00
e1b9597fdb crypt and adduser tool 2021-01-26 22:06:39 +01:00
ca9e0b81d3 application and pwhash 2021-01-26 21:27:17 +01:00
6 changed files with 134 additions and 26 deletions

View File

@ -24,7 +24,8 @@ RUN \
pip3 install uwsgi && \ pip3 install uwsgi && \
pip3 install flask-cors && \ pip3 install flask-cors && \
pip3 install six && \ pip3 install six && \
pip3 install python-jose[cryptography] pip3 install python-jose[cryptography] && \
pip3 install pbkdf2
RUN \ RUN \
mkdir -p ${APP_DIR} && \ mkdir -p ${APP_DIR} && \

67
asadduser.py Executable file
View File

@ -0,0 +1,67 @@
#!/usr/bin/python
import mariadb
from pbkdf2 import crypt
import argparse
import os
parser = argparse.ArgumentParser(description='asadduser')
parser.add_argument('--user', '-u',
help='Login',
required=True)
parser.add_argument('--password', '-p',
help='Password',
required=True)
parser.add_argument('--application', '-a',
help='Application',
required=True)
parser.add_argument('--issuer', '-i',
help='Issuer',
required=True)
args = parser.parse_args()
user = args.user
password = args.password
application = args.application
issuer = args.issuer
DB_USER = os.environ["DB_USER"]
DB_PASS = os.environ["DB_PASS"]
DB_HOST = os.environ["DB_HOST"]
DB_NAME = os.environ["DB_NAME"]
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.autocommit = False
cur = conn.cursor()
cur.execute("""
INSERT INTO users (issuer, login, password)
VALUES(
(SELECT id FROM issuers WHERE name = ?),
?,
?
)
""", [issuer, user, pwhash])
cur.execute("""
INSERT INTO user_applications_mapping (application, user)
VALUES(
(SELECT id FROM applications WHERE name = ?),
(SELECT id FROM users WHERE login = ?)
)
""", [application, user])
conn.commit()
finally:
if cur:
cur.close()
if conn:
conn.rollback()
conn.close()

51
auth.py
View File

@ -5,7 +5,7 @@ import werkzeug
import os import os
import mariadb import mariadb
from collections import namedtuple from collections import namedtuple
from pbkdf2 import crypt
DB_USER = os.environ["DB_USER"] DB_USER = os.environ["DB_USER"]
DB_PASS = os.environ["DB_PASS"] DB_PASS = os.environ["DB_PASS"]
@ -13,14 +13,21 @@ DB_HOST = os.environ["DB_HOST"]
DB_NAME = os.environ["DB_NAME"] DB_NAME = os.environ["DB_NAME"]
class NoUserException(Exception): pass class NoUserException(Exception):
class ManyUsersException(Exception): pass pass
class ManyUsersException(Exception):
pass
class PasswordMismatchException(Exception):
pass
UserEntry = namedtuple('UserEntry', ['id', 'login', 'issuer', 'secret', 'expiry', 'claims']) UserEntry = namedtuple('UserEntry', ['id', 'login', 'issuer', 'secret', 'expiry', 'claims'])
def getUserEntryFromDB(login: str, password: str) -> UserEntry: def getUserEntryFromDB(application: str, login: str):
conn = None conn = None
cur = None cur = None
try: try:
@ -29,9 +36,9 @@ def getUserEntryFromDB(login: str, password: str) -> UserEntry:
conn.autocommit = False conn.autocommit = False
cur = conn.cursor(dictionary=True) cur = conn.cursor(dictionary=True)
# print("DEBUG: getUserEntryFromDB: login: <{}>, password: <{}>".format(login, password)) cur.execute("SELECT id, password, issuer, secret, expiry FROM user_application_and_issuer " +
cur.execute("SELECT id, issuer, secret, expiry FROM user_and_issuer WHERE login = ? AND password = ?", " WHERE application = ? AND login = ?",
[login, password]) [application, login])
resObj = cur.next() resObj = cur.next()
print("DEBUG: getUserEntryFromDB: resObj: {}".format(resObj)) print("DEBUG: getUserEntryFromDB: resObj: {}".format(resObj))
if not resObj: if not resObj:
@ -55,11 +62,11 @@ def getUserEntryFromDB(login: str, password: str) -> UserEntry:
else: else:
claims[claimObj["key"]] = claimObj["value"] claims[claimObj["key"]] = claimObj["value"]
userEntry = UserEntry(id=userId, login=login, secret=resObj["secret"], userEntry = UserEntry(id=userId, login=login,
issuer=resObj["issuer"], expiry=resObj["expiry"], secret=resObj["secret"], issuer=resObj["issuer"],
claims=claims) expiry=resObj["expiry"], claims=claims)
return userEntry return userEntry, resObj["password"]
except mariadb.Error as err: except mariadb.Error as err:
raise Exception("Error when connecting to database: {}".format(err)) raise Exception("Error when connecting to database: {}".format(err))
finally: finally:
@ -69,18 +76,21 @@ def getUserEntryFromDB(login: str, password: str) -> UserEntry:
conn.rollback() conn.rollback()
conn.close() conn.close()
def getUserEntry(application, login, password):
def getUserEntry(login: str, password: str) -> UserEntry: userEntry, pwhash = getUserEntryFromDB(application, login)
return getUserEntryFromDB(login, password) if pwhash != crypt(password, pwhash):
raise PasswordMismatchException()
return userEntry
def generateToken(**args): def generateToken(**args):
try: try:
body = args["body"] body = args["body"]
application = body["application"]
login = body["login"] login = body["login"]
password = body["password"] password = body["password"]
userEntry = getUserEntryFromDB(login, password)
userEntry = getUserEntry(application, login, password)
timestamp = int(time.time()) timestamp = int(time.time())
payload = { payload = {
"iss": userEntry.issuer, "iss": userEntry.issuer,
@ -94,13 +104,16 @@ def generateToken(**args):
return jwt.encode(payload, userEntry.secret) return jwt.encode(payload, userEntry.secret)
except NoUserException: 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() raise werkzeug.exceptions.Unauthorized()
except ManyUsersException: except ManyUsersException:
print("ERROR: generateToken: too many users found") print("ERROR: generateToken: too many users found")
raise werkzeug.exceptions.Unauthorized() raise werkzeug.exceptions.Unauthorized()
except PasswordMismatchException:
print("ERROR: generateToken: wrong password")
raise werkzeug.exceptions.Unauthorized()
except KeyError: except KeyError:
print("ERROR: generateToken: login or password missing") print("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))) print("ERROR: generateToken: unspecific exception: {}".format(str(e)))

View File

@ -8,7 +8,14 @@ CREATE TABLE `issuers` (
) ENGINE=InnoDB; ) ENGINE=InnoDB;
ALTER TABLE `issuers` 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` ( CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
@ -40,6 +47,9 @@ ALTER TABLE `users`
ALTER TABLE `users` ALTER TABLE `users`
MODIFY COLUMN expiry int(10) unsigned NOT NULL; MODIFY COLUMN expiry int(10) unsigned NOT NULL;
ALTER TABLE `users`
MODIFY COLUMN expiry int(10) unsigned NOT NULL DEFAULT 600;
CREATE TABLE `claims` ( CREATE TABLE `claims` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`key` varchar(64) NOT NULL, `key` varchar(64) NOT NULL,
@ -58,6 +68,16 @@ CREATE TABLE `user_claims_mapping` (
REFERENCES `claims`(`id`) REFERENCES `claims`(`id`)
) ENGINE=InnoDB; ) 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 CREATE OR REPLACE VIEW claims_for_user AS
SELECT u.id AS user, SELECT u.id AS user,
c.`key` AS `key`, c.`key` AS `key`,
@ -68,14 +88,19 @@ CREATE OR REPLACE VIEW claims_for_user AS
WHERE m.user = u.id AND WHERE m.user = u.id AND
m.claim = c.id; 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, SELECT u.login AS login,
u.password AS password, u.password AS password,
u.id AS id, u.id AS id,
a.name as application,
i.name AS issuer, i.name AS issuer,
i.secret AS secret, i.secret AS secret,
least(u.expiry, i.max_expiry) AS expiry least(u.expiry, i.max_expiry) AS expiry
FROM users u, FROM users u,
issuers i issuers i,
WHERE u.issuer = i.id; applications a,
user_applications_mapping m
WHERE u.issuer = i.id AND
u.id = m.user AND
a.id = m.application;

View File

@ -45,9 +45,11 @@ components:
x-bearerInfoFunc: test.decodeToken x-bearerInfoFunc: test.decodeToken
schemas: schemas:
User: User:
description: Login/Password tuple description: Application/Login/Password tuple
type: object type: object
properties: properties:
application:
type: string
login: login:
type: string type: string
password: password:

View File

@ -3,7 +3,7 @@ from flask_cors import CORS
# instantiate the webservice # instantiate the webservice
app = connexion.App(__name__) app = connexion.App(__name__)
app.add_api('openapi.yaml') app.add_api('openapi.yaml', options = {"swagger_ui": False})
# CORSify it - otherwise Angular won't accept it # CORSify it - otherwise Angular won't accept it
CORS(app.app) CORS(app.app)