changes
This commit is contained in:
parent
b88dec12d3
commit
0d898061c6
6
ENV.tmpl
6
ENV.tmpl
@ -3,9 +3,7 @@
|
||||
export DB_HOST="172.16.10.18"
|
||||
export DB_USER="hausverwaltung-ui"
|
||||
export DB_PASS="test123"
|
||||
export DB_NAME="hausverwaltung"
|
||||
export DB_NAME="authservice"
|
||||
|
||||
export JWT_ISSUER='de.hottis.hausverwaltung'
|
||||
# only required for decoding, on client side
|
||||
export JWT_SECRET='streng_geheim'
|
||||
export JWT_LIFETIME_SECONDS=60
|
||||
export JWT_ALGORITHM='HS256'
|
||||
|
101
auth.py
101
auth.py
@ -1,13 +1,11 @@
|
||||
import time
|
||||
import connexion
|
||||
from jose import JWTError, jwt
|
||||
import werkzeug
|
||||
import os
|
||||
import mariadb
|
||||
from collections import namedtuple
|
||||
|
||||
JWT_ISSUER = os.environ['JWT_ISSUER']
|
||||
JWT_SECRET = os.environ['JWT_SECRET']
|
||||
JWT_LIFETIME_SECONDS = int(os.environ['JWT_LIFETIME_SECONDS'])
|
||||
JWT_ALGORITHM = os.environ['JWT_ALGORITHM']
|
||||
|
||||
DB_USER = os.environ["DB_USER"]
|
||||
DB_PASS = os.environ["DB_PASS"]
|
||||
@ -15,7 +13,14 @@ DB_HOST = os.environ["DB_HOST"]
|
||||
DB_NAME = os.environ["DB_NAME"]
|
||||
|
||||
|
||||
def getUserEntryFromDB(login, password):
|
||||
class NoUserException(Exception): pass
|
||||
class ManyUsersException(Exception): pass
|
||||
|
||||
UserEntry = namedtuple('UserEntry', ['id', 'login', 'issuer', 'secret', 'expiry', 'claims'])
|
||||
|
||||
|
||||
|
||||
def getUserEntryFromDB(login: str, password: str) -> UserEntry:
|
||||
conn = None
|
||||
cur = None
|
||||
try:
|
||||
@ -24,13 +29,35 @@ def getUserEntryFromDB(login, password):
|
||||
conn.autocommit = False
|
||||
|
||||
cur = conn.cursor(dictionary=True)
|
||||
cur.execute("SELECT id FROM users WHERE login = ? AND password = ?", [login, password])
|
||||
userEntry = cur.next()
|
||||
if not userEntry:
|
||||
raise Exception("No user entry found")
|
||||
# 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])
|
||||
resObj = cur.next()
|
||||
print("DEBUG: getUserEntryFromDB: resObj: {}".format(resObj))
|
||||
if not resObj:
|
||||
raise NoUserException()
|
||||
invObj = cur.next()
|
||||
if invObj:
|
||||
raise Exception("Too many user entries found")
|
||||
raise ManyUsersException()
|
||||
|
||||
userId = resObj["id"]
|
||||
cur.execute("SELECT user, `key`, `value` FROM claims_for_user where user = ?",
|
||||
[userId])
|
||||
claims = {}
|
||||
for claimObj in cur:
|
||||
print("DEBUG: getUserEntryFromDB: add claim {} -> {}".format(claimObj["key"], claimObj["value"]))
|
||||
if claimObj["key"] in claims:
|
||||
if isinstance(claimObj["key"], list):
|
||||
claims[claimObj["key"]].append(claimObj["value"])
|
||||
else:
|
||||
claims[claimObj["key"]] = [ claims[claimObj["key"]] ]
|
||||
claims[claimObj["key"]].append(claimObj["value"])
|
||||
else:
|
||||
claims[claimObj["key"]] = claimObj["value"]
|
||||
|
||||
userEntry = UserEntry(id=userId, login=login, secret=resObj["secret"],
|
||||
issuer=resObj["issuer"], expiry=resObj["expiry"],
|
||||
claims=claims)
|
||||
|
||||
return userEntry
|
||||
except mariadb.Error as err:
|
||||
@ -43,34 +70,38 @@ def getUserEntryFromDB(login, password):
|
||||
conn.close()
|
||||
|
||||
|
||||
def getUserEntry(login, password):
|
||||
def getUserEntry(login: str, password: str) -> UserEntry:
|
||||
return getUserEntryFromDB(login, password)
|
||||
|
||||
|
||||
def generateToken(login, password):
|
||||
userEntry = getUserEntryFromDB(login, password)
|
||||
userId = userEntry["id"]
|
||||
|
||||
timestamp = int(time.time())
|
||||
payload = {
|
||||
"iss": JWT_ISSUER,
|
||||
"iat": int(timestamp),
|
||||
"exp": int(timestamp + JWT_LIFETIME_SECONDS),
|
||||
"sub": str(userId),
|
||||
}
|
||||
return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
|
||||
|
||||
|
||||
def decodeToken(token):
|
||||
def generateToken(**args):
|
||||
try:
|
||||
return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
|
||||
except JWTError as e:
|
||||
return "Unauthorized ({})".format(str(e)), 401
|
||||
body = args["body"]
|
||||
login = body["login"]
|
||||
password = body["password"]
|
||||
userEntry = getUserEntryFromDB(login, password)
|
||||
|
||||
timestamp = int(time.time())
|
||||
payload = {
|
||||
"iss": userEntry.issuer,
|
||||
"iat": int(timestamp),
|
||||
"exp": int(timestamp + userEntry.expiry),
|
||||
"sub": str(userEntry.id)
|
||||
}
|
||||
for claim in userEntry.claims.items():
|
||||
# print("DEBUG: generateToken: add claim {} -> {}".format(claim[0], claim[1]))
|
||||
payload["x-{}".format(claim[0])] = claim[1]
|
||||
|
||||
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)
|
||||
|
||||
return jwt.encode(payload, userEntry.secret)
|
||||
except NoUserException:
|
||||
print("ERROR: generateToken: no user found, login or password wrong")
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
except ManyUsersException:
|
||||
print("ERROR: generateToken: too many users found")
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
except KeyError:
|
||||
print("ERROR: generateToken: login or password missing")
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
except Exception as e:
|
||||
print("ERROR: generateToken: unspecific exception: {}".format(str(e)))
|
||||
raise werkzeug.exceptions.Unauthorized()
|
||||
|
81
initial-schema.sql
Normal file
81
initial-schema.sql
Normal file
@ -0,0 +1,81 @@
|
||||
CREATE TABLE `issuers` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(128) NOT NULL,
|
||||
`secret` varchar(128) NOT NULL,
|
||||
`max_expiry` int(10) NOT NULL,
|
||||
CONSTRAINT PRIMARY KEY (`id`),
|
||||
CONSTRAINT UNIQUE KEY `uk_issuers_name` (`name`)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
ALTER TABLE `issuers`
|
||||
MODIFY COLUMN `max_expiry` int(10) unsigned NOT NULL;
|
||||
|
||||
CREATE TABLE `users` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`login` varchar(64) NOT NULL,
|
||||
`password` varchar(64) NOT NULL,
|
||||
CONSTRAINT PRIMARY KEY (`id`),
|
||||
CONSTRAINT UNIQUE KEY `uk_users_login` (`login`)
|
||||
) 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` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`key` varchar(64) NOT NULL,
|
||||
`value` varchar(1024) NOT NULL,
|
||||
CONSTRAINT PRIMARY KEY (`id`),
|
||||
CONSTRAINT UNIQUE KEY `uk_claims_key_value` (`key`, `value`)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE `user_claims_mapping` (
|
||||
`user` int(10) unsigned NOT NULL,
|
||||
`claim` int(10) unsigned NOT NULL,
|
||||
CONSTRAINT UNIQUE KEY `uk_user_claims_mapping` (`user`, `claim` ),
|
||||
CONSTRAINT FOREIGN KEY `fk_user_claims_mapping_user` (`user`)
|
||||
REFERENCES `users`(`id`),
|
||||
CONSTRAINT FOREIGN KEY `fk_user_claims_mapping_claim` (`claim`)
|
||||
REFERENCES `claims`(`id`)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE OR REPLACE VIEW claims_for_user AS
|
||||
SELECT u.id AS user,
|
||||
c.`key` AS `key`,
|
||||
c.`value` AS `value`
|
||||
FROM users u,
|
||||
claims c,
|
||||
user_claims_mapping m
|
||||
WHERE m.user = u.id AND
|
||||
m.claim = c.id;
|
||||
|
||||
CREATE OR REPLACE VIEW user_and_issuer AS
|
||||
SELECT u.login AS login,
|
||||
u.password AS password,
|
||||
u.id AS id,
|
||||
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;
|
||||
|
33
openapi.yaml
33
openapi.yaml
@ -4,24 +4,16 @@ info:
|
||||
version: "0.1"
|
||||
|
||||
paths:
|
||||
/auth/{login}:
|
||||
/auth:
|
||||
post:
|
||||
tags: [ "JWT" ]
|
||||
summary: Return JWT token
|
||||
operationId: auth.generateToken
|
||||
parameters:
|
||||
- name: login
|
||||
description: Login
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: password
|
||||
description: Password
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: '#/components/schemas/User'
|
||||
responses:
|
||||
'200':
|
||||
description: JWT token
|
||||
@ -33,7 +25,7 @@ paths:
|
||||
get:
|
||||
tags: [ "JWT" ]
|
||||
summary: Return secret string
|
||||
operationId: auth.getSecret
|
||||
operationId: test.getSecret
|
||||
responses:
|
||||
'200':
|
||||
description: secret response
|
||||
@ -50,4 +42,13 @@ components:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
x-bearerInfoFunc: auth.decodeToken
|
||||
x-bearerInfoFunc: test.decodeToken
|
||||
schemas:
|
||||
User:
|
||||
description: Login/Password tuple
|
||||
type: object
|
||||
properties:
|
||||
login:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
|
20
test.py
Normal file
20
test.py
Normal file
@ -0,0 +1,20 @@
|
||||
from jose import JWTError, jwt
|
||||
import os
|
||||
import werkzeug
|
||||
|
||||
|
||||
JWT_SECRET = os.environ['JWT_SECRET']
|
||||
|
||||
def decodeToken(token):
|
||||
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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user