forked from ScoDoc/ScoDoc
API v9: première esquisse
This commit is contained in:
parent
4ac076ec6c
commit
be224b9576
@ -117,6 +117,7 @@ def create_app(config_class=DevConfig):
|
||||
from app.views import notes_bp
|
||||
from app.views import users_bp
|
||||
from app.views import absences_bp
|
||||
from app.api import bp as api_bp
|
||||
|
||||
# https://scodoc.fr/ScoDoc
|
||||
app.register_blueprint(scodoc_bp)
|
||||
@ -130,6 +131,7 @@ def create_app(config_class=DevConfig):
|
||||
app.register_blueprint(
|
||||
absences_bp, url_prefix="/ScoDoc/<scodoc_dept>/Scolarite/Absences"
|
||||
)
|
||||
app.register_blueprint(api_bp, url_prefix="/ScoDoc/api")
|
||||
scodoc_exc_formatter = RequestFormatter(
|
||||
"[%(asctime)s] %(remote_addr)s requested %(url)s\n"
|
||||
"%(levelname)s in %(module)s: %(message)s"
|
||||
|
8
app/api/__init__.py
Normal file
8
app/api/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
"""api.__init__
|
||||
"""
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
bp = Blueprint("api", __name__)
|
||||
|
||||
from app.api import sco_api
|
53
app/api/auth.py
Normal file
53
app/api/auth.py
Normal file
@ -0,0 +1,53 @@
|
||||
# -*- coding: UTF-8 -*
|
||||
# Authentication code borrowed from Miguel Grinberg's Mega Tutorial
|
||||
# (see https://github.com/miguelgrinberg/microblog)
|
||||
|
||||
# Under The MIT License (MIT)
|
||||
|
||||
# Copyright (c) 2017 Miguel Grinberg
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth
|
||||
from app.auth.models import User
|
||||
from app.api.errors import error_response
|
||||
|
||||
basic_auth = HTTPBasicAuth()
|
||||
token_auth = HTTPTokenAuth()
|
||||
|
||||
|
||||
@basic_auth.verify_password
|
||||
def verify_password(username, password):
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if user and user.check_password(password):
|
||||
return user
|
||||
|
||||
|
||||
@basic_auth.error_handler
|
||||
def basic_auth_error(status):
|
||||
return error_response(status)
|
||||
|
||||
|
||||
@token_auth.verify_token
|
||||
def verify_token(token):
|
||||
return User.check_token(token) if token else None
|
||||
|
||||
|
||||
@token_auth.error_handler
|
||||
def token_auth_error(status):
|
||||
return error_response(status)
|
37
app/api/errors.py
Normal file
37
app/api/errors.py
Normal file
@ -0,0 +1,37 @@
|
||||
# Authentication code borrowed from Miguel Grinberg's Mega Tutorial
|
||||
# (see https://github.com/miguelgrinberg/microblog)
|
||||
|
||||
# Under The MIT License (MIT)
|
||||
|
||||
# Copyright (c) 2017 Miguel Grinberg
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.from flask import jsonify
|
||||
from werkzeug.http import HTTP_STATUS_CODES
|
||||
|
||||
|
||||
def error_response(status_code, message=None):
|
||||
payload = {"error": HTTP_STATUS_CODES.get(status_code, "Unknown error")}
|
||||
if message:
|
||||
payload["message"] = message
|
||||
response = jsonify(payload)
|
||||
response.status_code = status_code
|
||||
return response
|
||||
|
||||
|
||||
def bad_request(message):
|
||||
return error_response(400, message)
|
46
app/api/sco_api.py
Normal file
46
app/api/sco_api.py
Normal file
@ -0,0 +1,46 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gestion scolarite IUT
|
||||
#
|
||||
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""API ScoDoc 9
|
||||
"""
|
||||
# PAS ENCORE IMPLEMENTEE, juste un essai
|
||||
|
||||
from flask import jsonify, request, url_for, abort
|
||||
from app import db
|
||||
from app.api import bp
|
||||
from app.api.auth import token_auth
|
||||
from app.api.errors import bad_request
|
||||
|
||||
from app import models
|
||||
|
||||
|
||||
@bp.route("/ScoDoc/api/list_depts", methods=["GET"])
|
||||
@token_auth.login_required
|
||||
def list_depts():
|
||||
depts = models.Departement.query.filter_by(visible=True).all()
|
||||
data = {"items": [d.to_dict() for d in depts]}
|
||||
return jsonify(data)
|
20
app/api/tokens.py
Normal file
20
app/api/tokens.py
Normal file
@ -0,0 +1,20 @@
|
||||
from flask import jsonify
|
||||
from app import db
|
||||
from app.api import bp
|
||||
from app.api.auth import basic_auth, token_auth
|
||||
|
||||
|
||||
@bp.route("/tokens", methods=["POST"])
|
||||
@basic_auth.login_required
|
||||
def get_token():
|
||||
token = basic_auth.current_user().get_token()
|
||||
db.session.commit()
|
||||
return jsonify({"token": token})
|
||||
|
||||
|
||||
@bp.route("/tokens", methods=["DELETE"])
|
||||
@token_auth.login_required
|
||||
def revoke_token():
|
||||
token_auth.current_user().revoke_token()
|
||||
db.session.commit()
|
||||
return "", 204
|
@ -37,9 +37,11 @@ def login():
|
||||
if form.validate_on_submit():
|
||||
user = User.query.filter_by(user_name=form.user_name.data).first()
|
||||
if user is None or not user.check_password(form.password.data):
|
||||
current_app.logger.info("login: invalid (%s)", form.user_name.data)
|
||||
flash(_("Invalid user name or password"))
|
||||
return redirect(url_for("auth.login"))
|
||||
login_user(user, remember=form.remember_me.data)
|
||||
current_app.logger.info("login: success (%s)", form.user_name.data)
|
||||
next_page = request.args.get("next")
|
||||
if not next_page or url_parse(next_page).netloc != "":
|
||||
next_page = url_for("scodoc.index")
|
||||
|
@ -34,3 +34,13 @@ class Departement(db.Model):
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Departement {self.acronym}>"
|
||||
|
||||
def to_dict(self):
|
||||
data = {
|
||||
"id": self.id,
|
||||
"acronym": self.acronym,
|
||||
"description": self.description,
|
||||
"visible": self.visible,
|
||||
"date_creation": self.date_creation,
|
||||
}
|
||||
return data
|
||||
|
@ -52,6 +52,10 @@ def POST(s, path, data, errmsg=None):
|
||||
|
||||
# --- Ouverture session (login)
|
||||
s = requests.Session()
|
||||
s.post(
|
||||
"https://deb11.viennet.net/api/auth/login",
|
||||
data={"user_name": USER, "password": PASSWORD},
|
||||
)
|
||||
r = s.get(BASEURL, auth=(USER, PASSWORD), verify=CHECK_CERTIFICATE)
|
||||
if r.status_code != 200:
|
||||
raise ScoError("erreur de connection: vérifier adresse et identifiants")
|
||||
|
@ -18,6 +18,7 @@ Flask==2.0.1
|
||||
Flask-Babel==2.0.0
|
||||
Flask-Bootstrap==3.3.7.1
|
||||
Flask-Caching==1.10.1
|
||||
Flask-HTTPAuth==4.4.0
|
||||
Flask-Login==0.5.0
|
||||
Flask-Mail==0.9.1
|
||||
Flask-Migrate==3.1.0
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.0.11"
|
||||
SCOVERSION = "9.0.12"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user