Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
da06065959
|
|||
fe007cbfe7
|
|||
e2d5ed21ad
|
|||
003c83da92
|
|||
ca17c556d6
|
|||
ef1b8ddf30
|
|||
e1b9597fdb
|
|||
ca9e0b81d3
|
@ -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} && \
|
||||||
|
3
ENV.tmpl
3
ENV.tmpl
@ -1,9 +1,10 @@
|
|||||||
# copy to ENV and adjust values
|
# copy to ENV and adjust values
|
||||||
|
|
||||||
export DB_HOST="172.16.10.18"
|
export DB_HOST="172.16.10.18"
|
||||||
export DB_USER="hausverwaltung-ui"
|
export DB_USER="authservice-ui"
|
||||||
export DB_PASS="test123"
|
export DB_PASS="test123"
|
||||||
export DB_NAME="authservice"
|
export DB_NAME="authservice"
|
||||||
|
|
||||||
# only required for decoding, on client side
|
# only required for decoding, on client side
|
||||||
export JWT_SECRET='streng_geheim'
|
export JWT_SECRET='streng_geheim'
|
||||||
|
export JWT_ISSUER='de.hottis.authservice'
|
||||||
|
59
asadduser.py
Executable file
59
asadduser.py
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/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)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
user = args.user
|
||||||
|
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"]
|
||||||
|
|
||||||
|
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 (login, pwhash)
|
||||||
|
VALUES(?, ?)
|
||||||
|
""", [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()
|
||||||
|
|
58
auth.py
58
auth.py
@ -5,22 +5,32 @@ 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"]
|
||||||
DB_HOST = os.environ["DB_HOST"]
|
DB_HOST = os.environ["DB_HOST"]
|
||||||
DB_NAME = os.environ["DB_NAME"]
|
DB_NAME = os.environ["DB_NAME"]
|
||||||
|
|
||||||
|
JWT_ISSUER = os.environ["JWT_ISSUER"]
|
||||||
|
JWT_SECRET = os.environ["JWT_SECRET"]
|
||||||
|
|
||||||
class NoUserException(Exception): pass
|
|
||||||
class ManyUsersException(Exception): pass
|
|
||||||
|
|
||||||
UserEntry = namedtuple('UserEntry', ['id', 'login', 'issuer', 'secret', 'expiry', 'claims'])
|
class NoUserException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ManyUsersException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PasswordMismatchException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
UserEntry = namedtuple('UserEntry', ['id', 'login', '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 +39,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, pwhash, expiry FROM user_application" +
|
||||||
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 +65,9 @@ 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, expiry=resObj["expiry"], claims=claims)
|
||||||
issuer=resObj["issuer"], expiry=resObj["expiry"],
|
|
||||||
claims=claims)
|
|
||||||
|
|
||||||
return userEntry
|
return userEntry, resObj["pwhash"]
|
||||||
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,21 +77,24 @@ 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": JWT_ISSUER,
|
||||||
"iat": int(timestamp),
|
"iat": int(timestamp),
|
||||||
"exp": int(timestamp + userEntry.expiry),
|
"exp": int(timestamp + userEntry.expiry),
|
||||||
"sub": str(userEntry.id)
|
"sub": str(userEntry.id)
|
||||||
@ -92,15 +103,18 @@ def generateToken(**args):
|
|||||||
# print("DEBUG: generateToken: add claim {} -> {}".format(claim[0], claim[1]))
|
# print("DEBUG: generateToken: add claim {} -> {}".format(claim[0], claim[1]))
|
||||||
payload["x-{}".format(claim[0])] = claim[1]
|
payload["x-{}".format(claim[0])] = claim[1]
|
||||||
|
|
||||||
return jwt.encode(payload, userEntry.secret)
|
return jwt.encode(payload, JWT_ISSUER)
|
||||||
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)))
|
||||||
|
@ -1,45 +1,22 @@
|
|||||||
CREATE TABLE `issuers` (
|
CREATE DATABASE `authservice`;
|
||||||
|
USE `authservice`;
|
||||||
|
|
||||||
|
CREATE TABLE `applications` (
|
||||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`name` varchar(128) NOT NULL,
|
`name` varchar(128) NOT NULL,
|
||||||
`secret` varchar(128) NOT NULL,
|
|
||||||
`max_expiry` int(10) NOT NULL,
|
|
||||||
CONSTRAINT PRIMARY KEY (`id`),
|
CONSTRAINT PRIMARY KEY (`id`),
|
||||||
CONSTRAINT UNIQUE KEY `uk_issuers_name` (`name`)
|
CONSTRAINT UNIQUE KEY `uk_applications_name` (`name`)
|
||||||
) ENGINE=InnoDB;
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
ALTER TABLE `issuers`
|
|
||||||
MODIFY COLUMN `max_expiry` int(10) unsigned NOT NULL;
|
|
||||||
|
|
||||||
CREATE TABLE `users` (
|
CREATE TABLE `users` (
|
||||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`login` varchar(64) NOT NULL,
|
`login` varchar(64) NOT NULL,
|
||||||
`password` varchar(64) NOT NULL,
|
`pwhash` varchar(64) NOT NULL,
|
||||||
CONSTRAINT PRIMARY KEY (`id`),
|
`expiry` int(10) unsigned NOT NULL DEFAULT 600,
|
||||||
CONSTRAINT UNIQUE KEY `uk_users_login` (`login`)
|
CONSTRAINT PRIMARY KEY (`id`),
|
||||||
|
CONSTRAINT UNIQUE KEY `uk_users_login` (`login`)
|
||||||
) ENGINE=InnoDB;
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
ALTER TABLE `users`
|
|
||||||
ADD COLUMN issuer int(10) unsigned;
|
|
||||||
|
|
||||||
ALTER TABLE `users`
|
|
||||||
MODIFY COLUMN issuer int(10) unsigned NOT NULL;
|
|
||||||
|
|
||||||
ALTER TABLE `users`
|
|
||||||
ADD CONSTRAINT FOREIGN KEY `fk_users_issuer` (`issuer`)
|
|
||||||
REFERENCES `issuers` (`id`);
|
|
||||||
|
|
||||||
ALTER TABLE `users`
|
|
||||||
DROP CONSTRAINT `uk_users_login`;
|
|
||||||
|
|
||||||
ALTER TABLE `users`
|
|
||||||
ADD CONSTRAINT UNIQUE KEY `uk_users_login_issuer` (`login`, `issuer`);
|
|
||||||
|
|
||||||
ALTER TABLE `users`
|
|
||||||
ADD COLUMN expiry int(10) unsigned;
|
|
||||||
|
|
||||||
ALTER TABLE `users`
|
|
||||||
MODIFY COLUMN expiry int(10) unsigned NOT NULL;
|
|
||||||
|
|
||||||
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 +35,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 +55,57 @@ 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 AS
|
||||||
SELECT u.login AS login,
|
SELECT u.login AS login,
|
||||||
u.password AS password,
|
u.pwhash AS pwhash,
|
||||||
u.id AS id,
|
u.id AS id,
|
||||||
i.name AS issuer,
|
u.expiry AS expiry,
|
||||||
i.secret AS secret,
|
a.name as application
|
||||||
least(u.expiry, i.max_expiry) AS expiry
|
|
||||||
FROM users u,
|
FROM users u,
|
||||||
issuers i
|
applications a,
|
||||||
WHERE u.issuer = i.id;
|
user_applications_mapping m
|
||||||
|
WHERE u.id = m.user AND
|
||||||
|
a.id = m.application;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE USER 'authservice-ui'@'%' IDENTIFIED BY 'test123';
|
||||||
|
GRANT SELECT ON `user_application` TO 'authservice-ui'@'%';
|
||||||
|
GRANT SELECT ON `claims_for_user` TO 'authservice-ui'@'%';
|
||||||
|
|
||||||
|
CREATE USER 'authservice-cli'@'%' IDENTIFIED BY 'test123';
|
||||||
|
GRANT INSERT ON `users` TO 'authservice-cli'@'%';
|
||||||
|
GRANT INSERT ON `user_applications_mapping` TO 'authservice-cli'@'%';
|
||||||
|
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
|
||||||
|
INSERT INTO `applications` (`name`) VALUES ('hv');
|
||||||
|
|
||||||
|
INSERT INTO `claims` (`key`, `value`) VALUES ('accesslevel', 'r');
|
||||||
|
INSERT INTO `claims` (`key`, `value`) VALUES ('accesslevel', 'rw');
|
||||||
|
|
||||||
|
-- password is 'test123'
|
||||||
|
INSERT INTO `users` (`login`, `pwhash`) VALUES ('wn', '$p5k2$186a0$dJXL0AjF$0HualDF92nyilDXPgSbaUn/UpFzSrpPx');
|
||||||
|
INSERT INTO `user_applications_mapping` (`user`, `application`)
|
||||||
|
VALUES(
|
||||||
|
(SELECT `id` FROM `users` WHERE `login` = 'wn'),
|
||||||
|
(SELECT `id` FROM `applications` WHERE `name` = 'hv')
|
||||||
|
);
|
||||||
|
INSERT INTO `user_claims_mapping` (`user`, `claim`)
|
||||||
|
VALUES(
|
||||||
|
(SELECT `id` FROM `users` WHERE `login` = 'wn'),
|
||||||
|
(SELECT `id` FROM `claims` WHERE `key` = 'accesslevel' AND `value` = 'rw')
|
||||||
|
);
|
||||||
|
|
||||||
|
-- password is 'geheim'
|
||||||
|
INSERT INTO `users` (`login`, `pwhash`) VALUES ('gregor', '$p5k2$186a0$Tcwps8Ar$TsypGB.y1dCB9pWOPz2X2SsxYqrTn3Fv');
|
||||||
|
INSERT INTO `user_applications_mapping` (`user`, `application`)
|
||||||
|
VALUES(
|
||||||
|
(SELECT `id` FROM `users` WHERE `login` = 'gregor'),
|
||||||
|
(SELECT `id` FROM `applications` WHERE `name` = 'hv')
|
||||||
|
);
|
||||||
|
INSERT INTO `user_claims_mapping` (`user`, `claim`)
|
||||||
|
VALUES(
|
||||||
|
(SELECT `id` FROM `users` WHERE `login` = 'gregor'),
|
||||||
|
(SELECT `id` FROM `claims` WHERE `key` = 'accesslevel' AND `value` = 'rw')
|
||||||
|
);
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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": True})
|
||||||
|
|
||||||
# CORSify it - otherwise Angular won't accept it
|
# CORSify it - otherwise Angular won't accept it
|
||||||
CORS(app.app)
|
CORS(app.app)
|
||||||
|
Reference in New Issue
Block a user