From bd54344ddc24d518c05c5f508cc0af98d8e6f80e Mon Sep 17 00:00:00 2001
From: kaiyou <pierre@jaury.eu>
Date: Tue, 10 Sep 2019 22:26:20 +0200
Subject: [PATCH] Update the database models

---
 migrations/script.py.mako            |  4 +-
 migrations/versions/a95b3a78f983_.py | 68 ++++++++++++++++++++++++
 trurt/models.py                      | 78 ++++++++++++++++------------
 3 files changed, 115 insertions(+), 35 deletions(-)
 create mode 100644 migrations/versions/a95b3a78f983_.py

diff --git a/migrations/script.py.mako b/migrations/script.py.mako
index 2c015630..a78eb7db 100644
--- a/migrations/script.py.mako
+++ b/migrations/script.py.mako
@@ -1,15 +1,13 @@
-"""${message}
+""" ${message}
 
 Revision ID: ${up_revision}
 Revises: ${down_revision | comma,n}
 Create Date: ${create_date}
-
 """
 from alembic import op
 import sqlalchemy as sa
 ${imports if imports else ""}
 
-# revision identifiers, used by Alembic.
 revision = ${repr(up_revision)}
 down_revision = ${repr(down_revision)}
 branch_labels = ${repr(branch_labels)}
diff --git a/migrations/versions/a95b3a78f983_.py b/migrations/versions/a95b3a78f983_.py
new file mode 100644
index 00000000..a53916a7
--- /dev/null
+++ b/migrations/versions/a95b3a78f983_.py
@@ -0,0 +1,68 @@
+""" Create initial schemas
+
+Revision ID: a95b3a78f983
+Revises:
+Create Date: 2019-09-10 21:33:43.332000
+"""
+
+from alembic import op
+import sqlalchemy as sa
+
+
+revision = 'a95b3a78f983'
+down_revision = None
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    op.create_table('service',
+        sa.Column('spn', sa.String(length=255), nullable=True),
+        sa.Column('protocol', sa.String(length=25), nullable=True),
+        sa.Column('config', sa.String(), nullable=True),
+        sa.Column('id', sa.Integer(), nullable=False),
+        sa.Column('created_at', sa.Date(), nullable=False),
+        sa.Column('updated_at', sa.Date(), nullable=True),
+        sa.Column('comment', sa.String(length=255), nullable=True),
+        sa.PrimaryKeyConstraint('id'),
+        sa.UniqueConstraint('spn')
+    )
+    op.create_table('user',
+        sa.Column('username', sa.String(length=255), nullable=False),
+        sa.Column('id', sa.Integer(), nullable=False),
+        sa.Column('created_at', sa.Date(), nullable=False),
+        sa.Column('updated_at', sa.Date(), nullable=True),
+        sa.Column('comment', sa.String(length=255), nullable=True),
+        sa.PrimaryKeyConstraint('id'),
+        sa.UniqueConstraint('username')
+    )
+    op.create_table('auth',
+        sa.Column('user_id', sa.Integer(), nullable=True),
+        sa.Column('value', sa.String(), nullable=True),
+        sa.Column('extra', sa.String(), nullable=True),
+        sa.Column('id', sa.Integer(), nullable=False),
+        sa.Column('created_at', sa.Date(), nullable=False),
+        sa.Column('updated_at', sa.Date(), nullable=True),
+        sa.Column('comment', sa.String(length=255), nullable=True),
+        sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
+        sa.PrimaryKeyConstraint('id')
+    )
+    op.create_table('profile',
+        sa.Column('user_id', sa.Integer(), nullable=True),
+        sa.Column('service_id', sa.Integer(), nullable=True),
+        sa.Column('username', sa.String(length=255), nullable=False),
+        sa.Column('id', sa.Integer(), nullable=False),
+        sa.Column('created_at', sa.Date(), nullable=False),
+        sa.Column('updated_at', sa.Date(), nullable=True),
+        sa.Column('comment', sa.String(length=255), nullable=True),
+        sa.ForeignKeyConstraint(['service_id'], ['service.id'], ),
+        sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
+        sa.PrimaryKeyConstraint('id')
+    )
+
+
+def downgrade():
+    op.drop_table('profile')
+    op.drop_table('auth')
+    op.drop_table('user')
+    op.drop_table('service')
diff --git a/trurt/models.py b/trurt/models.py
index d702a739..af1dca26 100644
--- a/trurt/models.py
+++ b/trurt/models.py
@@ -1,16 +1,40 @@
-from sqlalchemy.ext import declarative
 from passlib import context, hash
-from datetime import datetime, date
 from flask import current_app as app
+from sqlalchemy.ext import declarative
+from datetime import date
 
 import flask_sqlalchemy
 import sqlalchemy
-import time
-import os
-import glob
 
 
-db = flask_sqlalchemy.SQLAlchemy()
+class Base(flask_sqlalchemy.Model):
+    """ Base class for all models
+    """
+    metadata = sqlalchemy.schema.MetaData(
+        naming_convention={
+            "fk": "%(table_name)s_%(column_0_name)s_fkey",
+            "pk": "%(table_name)s_pkey"
+        }
+    )
+
+    @declarative.declared_attr
+    def id(cls):
+        return sqlalchemy.Column(sqlalchemy.Integer(), primary_key=True)
+
+    @declarative.declared_attr
+    def created_at(cls):
+        return sqlalchemy.Column(sqlalchemy.Date, nullable=False, default=date.today)
+
+    @declarative.declared_attr
+    def updated_at(cls):
+        return sqlalchemy.Column(sqlalchemy.Date, nullable=True, onupdate=date.today)
+
+    @declarative.declared_attr
+    def comment(cls):
+        return sqlalchemy.Column(sqlalchemy.String(255), nullable=True)
+
+
+db = flask_sqlalchemy.SQLAlchemy(model_class=Base)
 
 
 class JSONEncoded(db.TypeDecorator):
@@ -26,38 +50,19 @@ class JSONEncoded(db.TypeDecorator):
         return json.loads(value) if value else None
 
 
-class Base(db.Model):
-    """ Base class for all models
-    """
-
-    __abstract__ = True
-
-    metadata = sqlalchemy.schema.MetaData(
-        naming_convention={
-            "fk": "%(table_name)s_%(column_0_name)s_fkey",
-            "pk": "%(table_name)s_pkey"
-        }
-    )
-
-    id = db.Column(db.Integer(), primary_key=True)
-    created_at = db.Column(db.Date, nullable=False, default=date.today)
-    updated_at = db.Column(db.Date, nullable=True, onupdate=date.today)
-    comment = db.Column(db.String(255), nullable=True)
-
-
-class User(Base):
+class User(db.Model):
     """ A user is the local representation of an authenticated person.
     """
     __tablename__ = "user"
 
     username = db.Column(db.String(255), nullable=False, unique=True)
 
-
     # Flask-login attributes
     is_authenticated = True
     is_active = True
     is_anonymous = False
 
+    @classmethod
     def get(cls, id):
         return cls.query.get(id)
 
@@ -65,12 +70,12 @@ class User(Base):
         return self.id
 
 
-class Auth(Base):
+class Auth(db.Model):
     """ An authenticator is a method to authenticate a user.
     """
     __tablename__ = "auth"
 
-    user_username = db.Column(db.Integer(), db.ForeignKey(User.id))
+    user_id = db.Column(db.Integer(), db.ForeignKey(User.id))
     user = db.relationship(User,
         backref=db.backref('auths', cascade='all, delete-orphan'))
     # TODO: support multiple authentication realms, therefore more than
@@ -78,8 +83,14 @@ class Auth(Base):
     value = db.Column(db.String)
     extra = db.Column(JSONEncoded)
 
+    def set_password(self, password):
+        self.value = hash.pbkdf2_sha256.hash(password)
+
+    def check_password(self, password):
+        return hash.pbkdf2_sha256.verify(password, self.password)
 
-class Service(Base):
+
+class Service(db.Model):
     """ A service is a client application (SP or RP typically).
     """
     __tablename__ = "service"
@@ -89,13 +100,16 @@ class Service(Base):
     config = db.Column(JSONEncoded)
 
 
-class Profile(Base):
+class Profile(db.Model):
     """ A profile is a user instance for a given service.
     """
     __tablename__ = "profile"
 
-    username = db.Column(db.String(255), nullable=False)
+    user_id = db.Column(db.Integer(), db.ForeignKey(User.id))
+    service_id = db.Column(db.Integer(), db.ForeignKey(Service.id))
     user = db.relationship(User,
         backref=db.backref('profiles', cascade='all, delete-orphan'))
     service = db.relationship(Service,
         backref=db.backref('profiles', cascade='all, delete-orphan'))
+
+    username = db.Column(db.String(255), nullable=False)
-- 
GitLab