1
0
forked from ScoDoc/ScoDoc

API users: password, plus de tests unitaires, correctifs.

This commit is contained in:
Emmanuel Viennet 2022-08-19 12:46:21 +02:00
parent 64f9de95a5
commit c6a99dc7d2
4 changed files with 140 additions and 6 deletions

View File

@ -14,9 +14,9 @@ from flask_login import current_user, login_required
from app import db, log from app import db, log
from app.api import api_bp as bp, api_web_bp from app.api import api_bp as bp, api_web_bp
from app.models.etudiants import Identite
from app.scodoc.sco_utils import json_error from app.scodoc.sco_utils import json_error
from app.auth.models import User, Role, UserRole from app.auth.models import User, Role, UserRole
from app.auth.models import is_valid_password
from app.decorators import scodoc, permission_required from app.decorators import scodoc, permission_required
from app.models import Departement from app.models import Departement
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
@ -38,7 +38,7 @@ def user_info(uid: int):
return json_error(404, "user not found") return json_error(404, "user not found")
if g.scodoc_dept: if g.scodoc_dept:
allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersView) allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersView)
if user.dept not in allowed_depts: if (None not in allowed_depts) and (user.dept not in allowed_depts):
return json_error(404, "user not found") return json_error(404, "user not found")
return jsonify(user.to_dict()) return jsonify(user.to_dict())
@ -109,7 +109,7 @@ def user_create():
if dept == "@all": if dept == "@all":
dept = None dept = None
allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersAdmin) allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersAdmin)
if dept not in allowed_depts: if (None not in allowed_depts) and (dept not in allowed_depts):
return json_error(403, "user_create: departement non autorise") return json_error(403, "user_create: departement non autorise")
if (dept is not None) and ( if (dept is not None) and (
Departement.query.filter_by(acronym=dept).first() is None Departement.query.filter_by(acronym=dept).first() is None
@ -168,6 +168,35 @@ def user_edit(uid: int):
return jsonify(user.to_dict()) return jsonify(user.to_dict())
@bp.route("/user/<int:uid>/password", methods=["POST"])
@api_web_bp.route("/user/<int:uid>/password", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoUsersAdmin)
def user_password(uid: int):
"""Modification du mot de passe d'un utilisateur
Champs modifiables:
{
"password": str
}
Si le mot de passe ne convient pas, erreur 400.
"""
data = request.get_json(force=True) # may raise 400 Bad Request
user: User = User.query.get_or_404(uid)
password = data.get("password")
if not password:
return json_error(404, "user_password: missing password")
if not is_valid_password(password):
return json_error(400, "user_password: invalid password")
allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersAdmin)
if (None not in allowed_depts) and ((user.dept not in allowed_depts)):
return json_error(403, "user_password: departement non autorise")
user.set_password(password)
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict())
@bp.route("/user/<int:uid>/role/<string:role_name>/add", methods=["POST"]) @bp.route("/user/<int:uid>/role/<string:role_name>/add", methods=["POST"])
@api_web_bp.route("/user/<int:uid>/role/<string:role_name>/add", methods=["POST"]) @api_web_bp.route("/user/<int:uid>/role/<string:role_name>/add", methods=["POST"])
@bp.route( @bp.route(

View File

@ -51,6 +51,7 @@ print(f"API URL={API_URL}")
HEADERS = get_auth_headers(API_USER, API_PASSWORD) HEADERS = get_auth_headers(API_USER, API_PASSWORD)
admin_h = get_auth_headers("admin_api", "admin_api")
departements = GET("/departements", headers=HEADERS) departements = GET("/departements", headers=HEADERS)
pp(departements) pp(departements)

View File

@ -76,10 +76,14 @@ def GET(path: str, headers: dict = None, errmsg=None, dept=None):
return r.json() # decode la reponse JSON return r.json() # decode la reponse JSON
def POST_JSON(path: str, data: dict = {}, headers: dict = None, errmsg=None): def POST_JSON(path: str, data: dict = {}, headers: dict = None, errmsg=None, dept=None):
"""Post""" """Post"""
if dept:
url = SCODOC_URL + f"/ScoDoc/{dept}/api" + path
else:
url = API_URL + path
r = requests.post( r = requests.post(
API_URL + path, url,
json=data, json=data,
headers=headers or {}, headers=headers or {},
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,

View File

@ -8,6 +8,7 @@ Utilisation :
from tests.api.setup_test_api import ( from tests.api.setup_test_api import (
API_URL, API_URL,
APIError,
CHECK_CERTIFICATE, CHECK_CERTIFICATE,
GET, GET,
POST_JSON, POST_JSON,
@ -83,7 +84,7 @@ def test_edit_users(api_admin_headers):
assert (nb_users + 1) == len(GET("/users/query", headers=admin_h)) assert (nb_users + 1) == len(GET("/users/query", headers=admin_h))
# Change le dept et rend inactif # Change le dept et rend inactif
user = POST_JSON( user = POST_JSON(
f"/user/edit/{user['id']}", f"/user/{user['id']}/edit",
{"active": False, "dept": "TAPI"}, {"active": False, "dept": "TAPI"},
headers=admin_h, headers=admin_h,
) )
@ -129,3 +130,102 @@ def test_roles(api_admin_headers):
assert set(role["permissions"]) == {"ScoView", "ScoAbsAddBillet"} assert set(role["permissions"]) == {"ScoView", "ScoAbsAddBillet"}
ans = POST_JSON("/role/Test_Y/delete", headers=admin_h) ans = POST_JSON("/role/Test_Y/delete", headers=admin_h)
assert ans["OK"] is True assert ans["OK"] is True
def test_modif_users_depts(api_admin_headers):
"""
Ce test vise à vérifier qu'un admin déclaré sur deux départements peut
bien modifier les utilisateurs de ses départements mais pas ceux des autres.
"""
admin_h = api_admin_headers
depts = GET("/departements", headers=admin_h)
assert len(depts) > 2
dept1, dept2, dept3 = depts[:3]
# On va utiliser les 3 1er dept (TAPI, AA, BB)
# On crée un nouvel utilisateur "chef2", admin dans les 2 premiers dept
# puis un utilisateur lambda, dans le dept 2 (AA)
chef2 = POST_JSON(
"/user/create",
{
"user_name": "chef2",
"nom": "Chef",
"prenom": "Test",
"dept": dept1["acronym"], # rattaché à dept1
},
headers=admin_h,
)
role_chef = POST_JSON(
"/role/create/chef",
{"permissions": ["ScoView", "ScoUsersAdmin", "ScoUsersView"]},
headers=admin_h,
)
_ = POST_JSON(
f"/user/{chef2['id']}/role/chef/add/departement/{dept1['acronym']}",
headers=admin_h,
)
_ = POST_JSON(
f"/user/{chef2['id']}/role/chef/add/departement/{dept2['acronym']}",
headers=admin_h,
)
# Un mot de passe trop simple:
ok = False
try:
_ = POST_JSON(
f"/user/{chef2['id']}/password",
{"password": "123456"},
headers=admin_h,
)
except APIError as exc:
if exc.args[1]["status"] == 400:
ok = True
assert ok
# Un "vrai" mot de passe:
chef2_password = "17HIOPpYhabb8qw'E:/jd7FFddjd"
_ = POST_JSON(
f"/user/{chef2['id']}/password",
{"password": chef2_password},
headers=admin_h,
)
# Création user lambda:
u_lambda = POST_JSON(
"/user/create",
{
"user_name": "lambda",
"nom": "Lambda",
"prenom": "Test",
"dept": dept2["acronym"],
},
headers=admin_h,
)
# Le chef va modifier u_lambda:
chef_h = get_auth_headers(chef2["user_name"], chef2_password)
# on utilise une URL avec département car on n'a pas le droit sur tous:
u = POST_JSON(
f"/user/{u_lambda['id']}/edit",
{"nom": "toto"},
headers=chef_h,
dept=dept1["acronym"],
)
assert u["nom"] == "toto"
# Dans l'autre ?
u = POST_JSON(
f"/user/{u_lambda['id']}/edit",
{"nom": "toto"},
headers=chef_h,
dept=dept2["acronym"],
)
# mais pas dans le troisième:
ok = False
try:
u = POST_JSON(
f"/user/{u_lambda['id']}/edit",
{"nom": "toto"},
headers=chef_h,
dept=dept3["acronym"],
)
except APIError as exc:
if exc.args[1]["status"] == 401:
ok = True
assert ok
# Nettoyage: