added user and group API and models

This commit is contained in:
2019-04-04 16:05:36 +02:00
parent cfa12717e0
commit 8b7b2f489c
12 changed files with 337 additions and 79 deletions

View File

@@ -19,6 +19,7 @@ coverage = "*"
flask-testing = "*"
flask-pyoidc = "*"
python-jose = "*"
flask-jwt-extended = "*"
[dev-packages]

93
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "e688fa75b0dde0fed147314666df07f60d9a4abf5fb8646ce3f2b85adf744a36"
"sha256": "bde392334efe0c90dfcb180d0fc07549827e819343af266b4fa43be0bc1c0596"
},
"pipfile-spec": 6,
"requires": {
@@ -38,10 +38,10 @@
},
"apispec": {
"hashes": [
"sha256:9300142aa93e0c020e6b223a196cd2103ac4a61bcceea7dba894c0959b72e327",
"sha256:bcfe21887ba7c6e94c4be00f10564478a0d9109bb8e574aae97442909fd69b31"
"sha256:6746a57f1395fc201d06b051e43e8a7fdd23138607ebbeabdfcc6477bc3cc956",
"sha256:f0a5ddaee255eebeda8e84659028c3523b7801c7023f9364972035d36b23d734"
],
"version": "==1.1.0"
"version": "==1.1.1"
},
"asn1crypto": {
"hashes": [
@@ -208,6 +208,13 @@
"index": "pypi",
"version": "==3.2.4"
},
"flask-jwt-extended": {
"hashes": [
"sha256:68035dba637fd58c0f0b308e49103cba7f6977aa301d958ddbd7b811a91c6dec"
],
"index": "pypi",
"version": "==3.18.0"
},
"flask-login": {
"hashes": [
"sha256:c815c1ac7b3e35e2081685e389a665f2c74d7e077cb93cecabaea352da4752ec"
@@ -309,9 +316,9 @@
},
"mako": {
"hashes": [
"sha256:4e02fde57bd4abb5ec400181e4c314f56ac3e49ba4fb8b0d50bba18cb27d25ae"
"sha256:04092940c0df49b01f43daea4f5adcecd0e50ef6a4b222be5ac003d5d84b2843"
],
"version": "==1.0.7"
"version": "==1.0.8"
},
"markupsafe": {
"hashes": [
@@ -348,10 +355,10 @@
},
"marshmallow": {
"hashes": [
"sha256:01412e979b45c003aeb3632718780b15b01566ae0182cc9232434b30f6b85e1b",
"sha256:8a1a2e13c6a621f4970faf21e5d9b146e451e779d0f334a96eae4fcdef53455f"
"sha256:0e497a6447ffaad55578138ca512752de7a48d12f444996ededc3d6bf8a09ca2",
"sha256:e21a4dea20deb167c723e0ffb13f4cf33bcbbeb8a334e92406a3308cedea2826"
],
"version": "==2.19.1"
"version": "==2.19.2"
},
"oic": {
"hashes": [
@@ -389,36 +396,36 @@
},
"pycryptodomex": {
"hashes": [
"sha256:0bda549e20db1eb8e29fb365d10acf84b224d813b1131c828fc830b2ce313dcd",
"sha256:1210c0818e5334237b16d99b5785aa0cee815d9997ee258bd5e2936af8e8aa50",
"sha256:2090dc8cd7843eae75bd504b9be86792baa171fc5a758ea3f60188ab67ca95cf",
"sha256:22e6784b65dfdd357bf9a8a842db445192b227103e2c3137a28c489c46742135",
"sha256:2edb8c3965a77e3092b5c5c1233ffd32de083f335202013f52d662404191ac79",
"sha256:310fe269ac870135ff610d272e88dcb594ee58f40ac237a688d7c972cbca43e8",
"sha256:456136b7d459f000794a67b23558351c72e21f0c2d4fcaa09fc99dae7844b0ef",
"sha256:463e49a9c5f1fa7bd36aff8debae0b5c487868c1fb66704529f2ad7e92f0cc9f",
"sha256:4a33b2828799ef8be789a462e6645ea6fe2c42b0df03e6763ccbfd1789c453e6",
"sha256:5ff02dff1b03929e6339226b318aa59bd0b5c362f96e3e0eb7f3401d30594ed3",
"sha256:6b1db8234b8ee2b30435d9e991389c2eeae4d45e09e471ffe757ba1dfae682bb",
"sha256:6eb67ee02de143cd19e36a52bd3869a9dc53e9184cd6bed5c39ff71dee2f6a45",
"sha256:6f42eea5afc7eee29494fdfddc6bb7173953d4197d9200e4f67096c2a24bc21b",
"sha256:87bc8082e2de2247df7d0b161234f8edb1384294362cc0c8db9324463097578b",
"sha256:8df93d34bc0e3a28a27652070164683a07d8a50c628119d6e0f7710f4d01b42f",
"sha256:989952c39e8fef1c959f0a0f85656e29c41c01162e33a3f5fd8ce71e47262ae9",
"sha256:a4a203077e2f312ec8677dde80a5c4e6fe5a82a46173a8edc8da668602a3e073",
"sha256:a793c1242dffd39f585ae356344e8935d30f01f6be7d4c62ffc87af376a2f5f9",
"sha256:b70fe991564e178af02ccf89435a8f9e8d052707a7c4b95bf6027cb785da3175",
"sha256:b83594196e3661cb78c97b80a62fbfbba2add459dfd532b58e7a7c62dd06aab4",
"sha256:ba27725237d0a3ea66ec2b6b387259471840908836711a3b215160808dffed0f",
"sha256:d1ab8ad1113cdc553ca50c4d5f0142198c317497364c0c70443d69f7ad1c9288",
"sha256:dce039a8a8a318d7af83cae3fd08d58cefd2120075dfac0ae14d706974040f63",
"sha256:e3213037ea33c85ab705579268cbc8a4433357e9fb99ec7ce9fdcc4d4eec1d50",
"sha256:ec8d8023d31ef72026d46e9fb301ff8759eff5336bcf3d1510836375f53f96a9",
"sha256:ece65730d50aa57a1330d86d81582a2d1587b2ca51cb34f586da8551ddc68fee",
"sha256:ed21fc515e224727793e4cc3fb3d00f33f59e3a167d3ad6ac1475ab3b05c2f9e",
"sha256:eec1132d878153d61a05424f35f089f951bd6095a4f6c60bdd2ef8919d44425e"
"sha256:00e0a7c992756c8d6d63f2cf992276ca62e702629bfcb54f57a192624c22c3d9",
"sha256:04689a34f5cf54bd049371c6b16ca44ed4c4202f3f9d58ff25e75016808d076e",
"sha256:088689b91b8dc0df52710e88ef447c6ca087c1f82398b8a41537c2a907f03e6a",
"sha256:1607d36788ff97b43abb896f7f81a2eed1bdc86e982e953e36b9afeb01942c9c",
"sha256:184356bb5039fc24c337dbe485b406425d6f0ee2659d59c313e69703063bf518",
"sha256:21cde61813059437206f745b620068d22cfe5059134eefe858ba7e4b639c410f",
"sha256:27a7968b231c1dec2a3d4cc66621c97ee623abda65fc1df392aa19a764f2e803",
"sha256:42c089411f55663c08e04c5bb8ce51d18002534ae76e8d2e1dd560b3d44dec5c",
"sha256:42e20e201267726c2d0687886d08f3e97192e0fa456535bb62274dd4b190dc0a",
"sha256:47ac3a9e87e559d1bfd60ebb5a81eac66f080ec5593c77d9e33b811bee929c87",
"sha256:73f72f7670d2182077fc36420e651ddc6eaa7c8478bf586719f051ecafec533e",
"sha256:84182dd94089287269a93f4d49226915b3aab386cba587dd8ec68da01bfa3e25",
"sha256:898c311e6fc8df2c4aba67c554276e5e798f8fb65d1854e26494f3d809314b91",
"sha256:96352d121e5a750b006e2a357d0a9913f12c6652ca07a5e61c74f63d8be0394f",
"sha256:9a18a7078cfdf61e020f0705c6b7188513973af68a26df42b7aaaa395b5e3e2f",
"sha256:9ecce5ab26737dde0beeff894344d56758e0711f2883619f10625d6a46d8116c",
"sha256:a4bac3507bcefd43e26a92182bcde27c4e37dbe4385b12ba04025a8bf93be5e7",
"sha256:ae044ff318656aaacdfce4d91c369ead871d170d03dfd44ee37e46734f06d24e",
"sha256:c8186e82d2854738ae7c18c9683bf023aa13d564b5c845a4cfd83063cf44e182",
"sha256:d2bc76e6edbcc8b32465c7e47f045398bb857390aca0782bfc6985919cf9d27c",
"sha256:d89db6097c644f814c9f8995a6a6e4b13dffcc82dcdb6ca06dc8a12545d36319",
"sha256:deb56cc3b9381b0d5a2060edb6e2ea31351c4b569abf52216d05c91a16214ac3",
"sha256:e3fe77a6558d21481d4c1290462eb3c9498bad46ce4cd979ff9dd2dd76b5700f",
"sha256:f04359d03506c4a8b0a7d178afda8dca78405bf6f8e8f2080d5206fc7733631a",
"sha256:f066c42bbc84c658d81aaecf36a14b71ef89a0353b17dc5c50f50fd7fb92fc15",
"sha256:f26d76c5430efc44b7a3ca4f5f661f3cb314a33bc2a9656f89f44bf73514446c",
"sha256:fab723651dfb40d25d8557bc0230133b29cf12835875cd56f77138ee467408cb",
"sha256:feb7673ee7981cee624c9cdb3446a7d909e99e7296eedef973e766a2bd5128ff"
],
"version": "==3.7.3"
"version": "==3.8.0"
},
"pyjwkest": {
"hashes": [
@@ -500,10 +507,10 @@
},
"sqlalchemy": {
"hashes": [
"sha256:781fb7b9d194ed3fc596b8f0dd4623ff160e3e825dd8c15472376a438c19598b"
"sha256:d5432832f91d200c3d8b473a266d59442d825f9ea744c467e68c5d9a9479fbce"
],
"index": "pypi",
"version": "==1.3.1"
"version": "==1.3.2"
},
"sqlalchemy-migrate": {
"hashes": [
@@ -543,10 +550,10 @@
},
"werkzeug": {
"hashes": [
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
"sha256:0a73e8bb2ff2feecfc5d56e6f458f5b99290ef34f565ffb2665801ff7de6af7a",
"sha256:7fad9770a8778f9576693f0cc29c7dcc36964df916b83734f4431c0e612a7fbc"
],
"version": "==0.14.1"
"version": "==0.15.2"
}
},
"develop": {}

View File

@@ -3,8 +3,10 @@
Backend base module
"""
from flask import Flask
import jwt
from flask import Flask, jsonify
from flask_httpauth import HTTPTokenAuth, HTTPBasicAuth, MultiAuth
from flask_jwt_extended import JWTManager, decode_token
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
@@ -16,9 +18,32 @@ db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
jwt_auth = HTTPTokenAuth()
# flask_jwt_extended: to be used usually by API
jwt_extended = JWTManager(app)
#
jwt_auth = HTTPTokenAuth('Bearer')
@jwt_auth.verify_token
def verify_token(token):
"""This function (and HTTPTokenAuth('Bearer')) has been defined to be used together with MultiAuth. For API calls
solely using JWT authentication, jwt_required of flask_jwt_extended should be used directly."""
app.logger.info(token)
try:
decoded = decode_token(token)
except jwt.exceptions.DecodeError as e:
app.logger.warn("Could not verify token: {}".format(str(e)))
return False
except jwt.exceptions.ExpiredSignatureError as e:
app.logger.warn("Could not verify token: {}".format(str(e)))
return False
app.logger.info(decoded)
return True
basic_auth = HTTPBasicAuth()
multi_auth = MultiAuth(basic_auth, jwt_auth)
from backend.auth import oidc_auth, auth_bp
oidc_auth.init_app(app)
@@ -26,11 +51,13 @@ oidc_auth.init_app(app)
# oidc_multi_auth = MultiAuth(oidc_auth, jwt_auth) <- can't work as OIDCAuthentication not implementing HTTPAuth
from .serve_frontend import fe_bp
from .api import auth_api_bp, api_bp
from .api import auth_api_bp, api_v1, api_bp
app.register_blueprint(auth_bp)
app.register_blueprint(auth_api_bp)
app.register_blueprint(api_bp)
app.register_blueprint(fe_bp)
# Fix flask-restplus by duck typing error handlers
jwt_extended._set_error_handler_callbacks(api_v1)

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from flask import Blueprint
from flask_restplus import Api
from flask_restplus import Api, Namespace
api_authorizations = {
'apikey': {
@@ -25,7 +25,15 @@ api_bp = Blueprint('api', __name__, url_prefix='/api')
api_v1 = Api(api_bp, prefix="/v1", version='0.1', title='Vue Test API',
description='The Vue Test API', doc='/v1/doc/', authorizations=api_authorizations, security='bearerAuth')
api_user = Namespace('user', description="User management namespace", authorizations=api_authorizations)
api_v1.add_namespace(api_user)
auth_api_bp = Blueprint('auth_api', __name__, url_prefix='/api/auth')
user_api_bp = Blueprint('user_api', __name__, url_prefix='/api/user')
group_api_bp = Blueprint('group_api', __name__, url_prefix='/api/group')
from .example_api import *
from .auth_api import *
from .user_api import *
from .group_api import *

View File

@@ -5,10 +5,14 @@ For example: listing of available auth providers or registration of users.
Login through API does not start a new session, but instead returns JWT.
"""
import base64
import json
import flask
from datetime import datetime, timedelta
import jwt
from flask import request, jsonify, current_app, url_for
from flask import request, jsonify, current_app, url_for, Response, session, redirect, make_response
from flask_jwt_extended import create_access_token, create_refresh_token, jwt_refresh_token_required, get_jwt_identity
from functools import wraps
from random import randint
@@ -22,14 +26,6 @@ from backend.auth import AUTH_PROVIDERS, oidc_auth
from backend.models.user_model import User, Group
def create_jwt(user: User, validity_min=30):
return jwt.encode({
'sub': user.email,
'iat': datetime.utcnow(),
'exp': datetime.utcnow() + timedelta(minutes=validity_min)},
current_app.config['SECRET_KEY'])
@auth_api_bp.route('/providers', methods=('GET',))
def get_auth_providers():
providers = dict()
@@ -65,8 +61,11 @@ def login():
if not user:
return jsonify({'message': 'Invalid credentials', 'authenticated': False}), 401
token = create_jwt(user)
return jsonify({'token': token.decode('UTF-8')})
token = {
'access_token': create_access_token(identity=user.email, fresh=True),
'refresh_token': create_refresh_token(identity=user.email)
}
return jsonify(token), 200
def check_and_create_groups(groups: Iterable[str]):
@@ -112,17 +111,38 @@ def create_or_retrieve_user_from_userinfo(userinfo):
@auth_api_bp.route('/oidc', methods=['GET'])
@auth_api_bp.route('/oidc/<redirect_url>', methods=['GET'])
@oidc_auth.oidc_auth()
def oidc():
def oidc(redirect_url=None):
user = create_or_retrieve_user_from_userinfo(flask.session['userinfo'])
#return jsonify(user.to_dict())
return user.toJSON()
if user is None:
return "Could not authenticate: could not find or create user.", 401
if current_app.config.get("AUTH_RETURN_EXTERNAL_JWT", False):
token = jwt.encode(flask.session['id_token'], current_app.config['SECRET_KEY'])
else:
token = create_jwt(user)
return token
token = json.dumps({
'access_token': create_access_token(identity=user.email, fresh=True),
'refresh_token': create_refresh_token(identity=user.email)
})
if redirect_url is None:
redirect_url = request.headers.get("Referer")
if redirect_url is None:
redirect_url = request.args.get('redirect_url')
if redirect_url is None:
redirect_url = "/"
app.logger.info("Token: {}".format(token))
response = make_response(redirect(redirect_url))
response.set_cookie('tokens', base64.b64encode(token.encode('utf-8')))
return response
@app.route('/refresh', methods=['POST'])
@jwt_refresh_token_required
def refresh():
"""Refresh token endpoint. This will generate a new access token from
the refresh token, but will mark that access token as non-fresh,
as we do not actually verify a password in this endpoint."""
current_user = get_jwt_identity()
new_token = create_access_token(identity=current_user, fresh=False)
ret = {'access_token': new_token}
return jsonify(ret), 200

40
api/group_api.py Normal file
View File

@@ -0,0 +1,40 @@
# Copyright (c) 2019. Tobias Kurze
"""
This module provides functions related to authentication through the API.
For example: listing of available auth providers or registration of users.
Login through API does not start a new session, but instead returns JWT.
"""
import flask
from datetime import datetime, timedelta
import jwt
from flask import request, jsonify, current_app, url_for
from flask_jwt_extended import jwt_required
from functools import wraps
from random import randint
from flask_login import logout_user, login_user
from typing import Iterable
from werkzeug.routing import BuildError
from backend import db, app
from backend.api import auth_api_bp, group_api_bp
from backend.auth import AUTH_PROVIDERS, oidc_auth
from backend.models.user_model import User, Group
@group_api_bp.route('/<id>', methods=['GET'])
@jwt_required
def get_group():
user = create_or_retrieve_user_from_userinfo(flask.session['userinfo'])
return jsonify(user.to_dict())
if user is None:
return "Could not authenticate: could not find or create user.", 401
if current_app.config.get("AUTH_RETURN_EXTERNAL_JWT", False):
token = jwt.encode(flask.session['id_token'], current_app.config['SECRET_KEY'])
else:
token = create_jwt(user)
return token

58
api/user_api.py Normal file
View File

@@ -0,0 +1,58 @@
# Copyright (c) 2019. Tobias Kurze
"""
This module provides functions related to authentication through the API.
For example: listing of available auth providers or registration of users.
Login through API does not start a new session, but instead returns JWT.
"""
import flask
import jwt
from flask import request, jsonify, current_app, url_for
from flask_jwt_extended import get_jwt_identity, jwt_optional, jwt_required
from flask_restplus import Resource, fields
from backend import db, app, jwt_auth
from backend.api import user_api_bp, api_bp, api_user
from backend.auth import oidc_auth
from backend.models.user_model import User, Group
user = api_user.model('User', {
'id': fields.String(required=True, description='The user\'s identifier'),
'first_name': fields.String(required=True, description='The user\'s first name'),
})
@api_user.route('/')
class UserList(Resource):
"""
This is a test class.
"""
#@jwt_auth.login_required
@jwt_required
@api_user.doc('users')
@api_user.marshal_list_with(user)
def get(self):
"""
just a test!
:return: Hello: World
"""
current_user = get_jwt_identity()
app.logger.info(current_user)
return User.get_all()
@api_user.route('/<id>')
@api_user.param('id', 'The user identifier')
@api_user.response(404, 'User not found')
class UserResource(Resource):
@jwt_auth.login_required
@api_user.doc('get_user')
@api_user.marshal_with(user)
def get(self, id):
"""Fetch a user given its identifier"""
user = User.get_by_id(id)
if user is not None:
return user
api_user.abort(404)
# api_user.add_resource(UserResource, '/')

BIN
app.db

Binary file not shown.

View File

@@ -4,7 +4,7 @@ OIDC login auth module
"""
import flask
from flask import jsonify
from flask import jsonify, redirect, url_for
from flask_login import login_user
from flask_pyoidc.flask_pyoidc import OIDCAuthentication
from flask_pyoidc.user_session import UserSession
@@ -15,17 +15,28 @@ from . import auth_bp
from .oidc_config import PROVIDER_NAME, OIDC_PROVIDERS
OIDCAuthentication.oidc_auth_orig = OIDCAuthentication.oidc_auth
OIDCAuthentication.oidc_logout_orig = OIDCAuthentication.oidc_logout
def oidc_auth_default_provider(self):
"""monkey patch oidc_auth"""
return self.oidc_auth_orig(PROVIDER_NAME)
OIDCAuthentication.oidc_auth_orig = OIDCAuthentication.oidc_auth
def oidc_logout_default_provider(self):
"""monkey patch oidc_logout"""
return self.oidc_logout_orig(PROVIDER_NAME)
OIDCAuthentication.oidc_auth = oidc_auth_default_provider
OIDCAuthentication.oidc_logout = oidc_logout_default_provider
oidc_auth = OIDCAuthentication(OIDC_PROVIDERS)
def create_or_retrieve_user_from_userinfo(userinfo):
"""Updates and returns ar creates a user from userinfo (part of OIDC token)."""
try:
email = userinfo["email"]
except KeyError:
@@ -34,6 +45,7 @@ def create_or_retrieve_user_from_userinfo(userinfo):
if user is not None:
app.logger.info("user found")
#TODO: update user!
return user
user = User(email=email, first_name=userinfo.get("given_name", ""),
@@ -57,3 +69,9 @@ def oidc():
return jsonify(id_token=user_session.id_token,
access_token=flask.session['access_token'],
userinfo=user_session.userinfo)
@auth_bp.route('/oidc_logout', methods=['GET'])
def oidc_logout():
oidc_auth.oidc_logout()
return redirect('/')

42
auth/utils.py Normal file
View File

@@ -0,0 +1,42 @@
import flask_jwt_extended
from flask_jwt_extended import jwt_optional, get_jwt_identity
from functools import wraps
from backend import jwt_auth
from backend.models.user_model import User
def requires_permission_level(permission_level):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if flask_jwt_extended.verify_jwt_in_request():
current_user_id = get_jwt_identity()
user = User.get_by_identifier(current_user_id)
if user is not None:
if user.has_permission(permission_level):
#for g in user.groups:
# if g.permissions
#TODO
pass
else:
pass
# return FALSE
#if not session.get('email'):
# return redirect(url_for('users.login'))
#user = User.find_by_email(session['email'])
#elif not user.allowed(access_level):
# return redirect(url_for('users.profile', message="You do not have access to that page. Sorry!"))
return f(*args, **kwargs)
return decorated_function
return decorator
def require_jwt():
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
return jwt_auth.login_required(jwt_optional(f(*args, **kwargs)))
return decorated_function
return decorator

View File

@@ -64,9 +64,11 @@ class Config():
#ASSETS_DEBUG = True
JWT_SECRET = "abcxyz"
JWT_ALGORITHM = "HS256"
JWT_EXP_DELTA_SECONDS = 5 * 60
#JWT_SECRET = "abcxyz"
#JWT_ALGORITHM = "HS256"
#JWT_EXP_DELTA_SECONDS = 5 * 60
JWT_SECRET_KEY = "abcxyz"
AUTH_RETURN_EXTERNAL_JWT = False

View File

@@ -46,23 +46,38 @@ user_group_table = db.Table('user_group',
primary_key=True))
# This is the association table for the many-to-many relationship between
# groups and permissions.
group_permission_table = db.Table('group_permission',
db.Column('group_id', db.Integer,
db.ForeignKey('group.id',
onupdate="CASCADE",
ondelete="CASCADE"),
primary_key=True),
db.Column('permission_id', db.Integer,
db.ForeignKey('permission.id',
onupdate="CASCADE",
ondelete="CASCADE"),
primary_key=True))
class User(UserMixin, db.Model):
"""
Example user model representation.
"""
id = db.Column(db.Integer, primary_key=True)
social_id = db.Column(db.String(64), nullable=True, unique=True)
nickname = db.Column(db.String(64), index=True, unique=True)
first_name = db.Column(db.String(64), index=True, nullable=True)
last_name = db.Column(db.String(64), index=True, nullable=True)
social_id = db.Column(db.Unicode(63), nullable=True, unique=True)
nickname = db.Column(db.Unicode(63), index=True, unique=True)
first_name = db.Column(db.Unicode(63), index=True, nullable=True)
last_name = db.Column(db.Unicode(63), index=True, nullable=True)
email = db.Column(db.String(120), nullable=False, index=True, unique=True)
lang = db.Column(db.String(16), index=False, unique=False)
timezone = db.Column(db.String(32), index=False, unique=False)
lang = db.Column(db.Unicode(32), index=False, unique=False)
timezone = db.Column(db.Unicode(63), index=False, unique=False)
posts = db.relationship('Post', backref='author', lazy='dynamic')
example_data_item = db.relationship('ExampleDataItem', backref='owner')
example_data_item_id = db.Column(db.ForeignKey(ExampleDataItem.id))
about_me = db.Column(db.String(140))
role = db.Column(db.String(64))
about_me = db.Column(db.Unicode(255))
role = db.Column(db.Unicode(63))
groups = db.relationship('Group', secondary=user_group_table, back_populates='users')
password = db.Column(db.String(255), nullable=True)
registered_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
@@ -110,6 +125,16 @@ class User(UserMixin, db.Model):
User.email == identifier,
User.id == identifier)).first()
@staticmethod
@login_manager.user_loader
def get_by_id(identifier):
"""
Find user by ID.
:param identifier:
:return:
"""
return User.query.filter(User.id == identifier).first()
@staticmethod
def get_all():
"""
@@ -396,8 +421,9 @@ class Group(db.Model):
super(Group, self).__init__(**kwargs)
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
name = db.Column(db.Unicode(64), unique=True, nullable=False)
name = db.Column(db.Unicode(63), unique=True, nullable=False)
users = db.relationship('User', secondary=user_group_table, back_populates='groups')
permissions = db.relationship('Permission', secondary=group_permission_table, back_populates='groups')
@staticmethod
def get_by_name(name):
@@ -417,3 +443,12 @@ class Group(db.Model):
def toJSON(self):
return json.dumps(self.to_dict(), default=lambda o: o.__dict__,
sort_keys=True, indent=4)
class Permission(db.Model):
"""Table containing permissions associated with groups."""
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
name = db.Column(db.Unicode(63), unique=True, nullable=False)
description = db.Column(db.Unicode(255))
groups = db.relationship(Group, secondary=group_permission_table,
back_populates='permissions')