migrate ScoDoc7 user db

This commit is contained in:
Emmanuel Viennet 2021-07-05 00:07:17 +02:00
parent 4526a6b934
commit 7f6a21f040
6 changed files with 115 additions and 8 deletions

View File

@ -5,7 +5,6 @@
import base64 import base64
from datetime import datetime, timedelta from datetime import datetime, timedelta
from hashlib import md5
import json import json
import os import os
import re import re
@ -40,6 +39,7 @@ class User(UserMixin, db.Model):
active = db.Column(db.Boolean, default=True, index=True) active = db.Column(db.Boolean, default=True, index=True)
password_hash = db.Column(db.String(128)) password_hash = db.Column(db.String(128))
password_scodoc7 = db.Column(db.String(42))
last_seen = db.Column(db.DateTime, default=datetime.utcnow) last_seen = db.Column(db.DateTime, default=datetime.utcnow)
date_modif_passwd = db.Column(db.DateTime, default=datetime.utcnow) date_modif_passwd = db.Column(db.DateTime, default=datetime.utcnow)
date_created = db.Column(db.DateTime, default=datetime.utcnow) date_created = db.Column(db.DateTime, default=datetime.utcnow)
@ -55,7 +55,6 @@ class User(UserMixin, db.Model):
self.roles = [] self.roles = []
self.user_roles = [] self.user_roles = []
super(User, self).__init__(**kwargs) super(User, self).__init__(**kwargs)
self._format_noms()
# Ajoute roles: # Ajoute roles:
if ( if (
not self.roles not self.roles
@ -89,6 +88,18 @@ class User(UserMixin, db.Model):
""" """
if not self.active: # inactived users can't login if not self.active: # inactived users can't login
return False return False
if (not self.password_hash) and self.password_scodoc7:
# Special case: user freshly migrated from ScoDoc7
if scu.check_scodoc7_password(self.password_scodoc7, password):
current_app.logger.warning(
"migrating legacy ScoDoc7 password for {}".format(self)
)
self.set_password(password)
self.password_scodoc7 = None
db.session.add(self)
db.session.commit()
return True
return False
if not self.password_hash: # user without password can't login if not self.password_hash: # user without password can't login
return False return False
return check_password_hash(self.password_hash, password) return check_password_hash(self.password_hash, password)
@ -161,7 +172,6 @@ class User(UserMixin, db.Model):
for r_d in data["roles_string"].split(","): for r_d in data["roles_string"].split(","):
role, dept = UserRole.role_dept_from_string(r_d) role, dept = UserRole.role_dept_from_string(r_d)
self.add_role(role, dept) self.add_role(role, dept)
self._format_noms()
def get_token(self, expires_in=3600): def get_token(self, expires_in=3600):
now = datetime.utcnow() now = datetime.utcnow()
@ -342,10 +352,10 @@ class Role(db.Model):
def insert_roles(): def insert_roles():
"""Create default roles""" """Create default roles"""
default_role = "Observateur" default_role = "Observateur"
for r, permissions in SCO_ROLES_DEFAULTS.items(): for role_name, permissions in SCO_ROLES_DEFAULTS.items():
role = Role.query.filter_by(name=r).first() role = Role.query.filter_by(name=role_name).first()
if role is None: if role is None:
role = Role(name=r) role = Role(name=role_name)
role.reset_permissions() role.reset_permissions()
for perm in permissions: for perm in permissions:
role.add_permission(perm) role.add_permission(perm)

View File

@ -28,10 +28,12 @@
""" Common definitions """ Common definitions
""" """
import base64
import bisect import bisect
import copy import copy
import datetime import datetime
import json import json
from hashlib import md5
import numbers import numbers
import os import os
import re import re
@ -672,6 +674,16 @@ def get_scodoc_version():
return os.popen("cd %s; ./get_scodoc_version.sh -s" % SCO_TOOLS_DIR).read().strip() return os.popen("cd %s; ./get_scodoc_version.sh -s" % SCO_TOOLS_DIR).read().strip()
def check_scodoc7_password(scodoc7_hash, password):
"""Check a password vs scodoc7 hash
used only during old databases migrations"""
m = md5()
m.update(password.encode("utf-8"))
# encodestring à remplacer par encodebytes #py3
h = base64.encodestring(m.digest()).decode("utf-8").strip()
return h == scodoc7_hash
# Simple string manipulations # Simple string manipulations
# on utf-8 encoded python strings # on utf-8 encoded python strings
# (yes, we should only use unicode strings, but... we use only strings) # (yes, we should only use unicode strings, but... we use only strings)

View File

@ -4,8 +4,11 @@
{% block app_content %} {% block app_content %}
<h2>ScoDoc: gestion scolarité</h2> <h2>ScoDoc: gestion scolarité</h2>
<p>Bonjour <font color="red"><b>{{current_user.get_nomcomplet()}}</b></font>.</p> {% if not current_user.is_anonymous %}
<p>Bonjour <font color="red"><b>{{current_user.get_nomcomplet()}}</b>
</font>.</p>
<p>N'oubliez pas de vous <a href="{{url_for('auth.logout')}}">déconnecter</a> après usage.</p> <p>N'oubliez pas de vous <a href="{{url_for('auth.logout')}}">déconnecter</a> après usage.</p>
{% endif %}
<ul class="main"> <ul class="main">
{% for dept in dept_ids %} {% for dept in dept_ids %}

7
app/utils/__init__.py Normal file
View File

@ -0,0 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
# Utilitaires divers, à utiliser en ligne de commande
# via flask
from app.utils.import_scodoc7_user_db import import_scodoc7_user_db

View File

@ -0,0 +1,64 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
import pdb
import re
import psycopg2
import psycopg2.extras
from flask import current_app
from app import app, db
from app.auth.models import User, Role
def import_scodoc7_user_db(scodoc7_db="dbname=SCOUSERS"):
"""Create users from existing ScoDoc7 db (SCOUSERS)
The resulting users are in SCO8USERS,
handled via Flask/SQLAlchemy ORM.
"""
cnx = psycopg2.connect(scodoc7_db)
cursor = cnx.cursor(cursor_factory=psycopg2.extras.DictCursor)
cursor.execute("SELECT * FROM sco_users;")
for u7 in cursor:
if User.query.filter_by(user_name=u7["user_name"]).first():
# user with same name exists !
current_app.logger.warning(
"User {} exists and is left unchanged".format(u7["user_name"])
)
else:
u = User(
user_name=u7["user_name"],
email=u7["email"],
date_modif_passwd=u7["date_modif_passwd"],
nom=u7["nom"],
prenom=u7["prenom"],
dept=u7["dept"],
passwd_temp=u7["passwd_temp"],
date_expiration=u7["date_expiration"],
password_scodoc7=u7["passwd"],
active=(u7["status"] == None),
)
# Set roles:
# ScoDoc7 roles are stored as 'AdminRT,EnsRT'
for role_dept in u7["roles"].split(","):
m = re.match(r"^([A-Za-z0-9]+?)([A-Z][A-Za-z0-9]*?)$", role_dept)
if not m:
current_app.logger.warning(
"User {}: ignoring role {}".format(u7["user_name"], role_dept)
)
else:
role_name = m.group(1)
dept = m.group(2)
role = Role.query.filter_by(name=role_name).first()
if not role:
current_app.logger.warning(
"User {}: ignoring role {}".format(
u7["user_name"], role_dept
)
)
else:
u.add_role(role, dept)
db.session.add(u)
current_app.logger.info("imported user {}".format(u))
db.session.commit()

View File

@ -20,6 +20,7 @@ from app import create_app, cli, db
from app.auth.models import User, Role, UserRole from app.auth.models import User, Role, UserRole
from app.views import notes, scolar, absences from app.views import notes, scolar, absences
import app.utils as utils
from config import Config from config import Config
@ -44,7 +45,7 @@ def make_shell_context():
@app.cli.command() @app.cli.command()
def user_db_init(): def user_db_init(): # user-db-init
"""Initialize the users database.""" """Initialize the users database."""
click.echo("Init the db") click.echo("Init the db")
# Create roles: # Create roles:
@ -166,3 +167,13 @@ def test_interactive(filename=None):
exec(open(filename).read()) exec(open(filename).read())
click.echo("Done.") click.echo("Done.")
@app.cli.command()
@with_appcontext
def user_db_import_scodoc7(): # user-db-import-scodoc7
"""Import used defined in ScoDoc7 postgresql database into ScoDoc8
The old database SCOUSERS must be alive and readable by the current user.
This script is typically run as unix user www-data.
"""
utils.import_scodoc7_user_db()