Skip to content

Commit

Permalink
Angel/ability to remove user from web (#44)
Browse files Browse the repository at this point in the history
* feat: write delete user from web and tests

* test: fix errors on multiple tests
  • Loading branch information
Angel-Dijoux authored Apr 3, 2024
1 parent 666a0a7 commit bb007a3
Show file tree
Hide file tree
Showing 17 changed files with 854 additions and 571 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ apispec = "*"
chardet = "*"
charset-normalizer = "*"
strawberry-graphql = {extras = ["debug-server"], version = "*"}
flask-wtf = "*"

[dev-packages]

Expand Down
1,004 changes: 521 additions & 483 deletions Pipfile.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bruno_onisep_explorer/getUserToken.bru
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ post {

body:json {
{
"username": "ELki",
"email": "[email protected]",
"password": "elki12345"
}
}
84 changes: 45 additions & 39 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
alembic==1.13.1
aniso8601==9.0.1
anyio==4.2.0
apispec==6.4.0
apispec-webframeworks==1.0.0
anyio==4.3.0
apispec==6.6.0
apispec-webframeworks==1.1.0
attrs==23.2.0
beautifulsoup4==4.12.2
beautifulsoup4==4.12.3
black==23.1.0
certifi==2023.11.17
certifi==2024.2.2
chardet==5.2.0
charset-normalizer==3.3.2
click==8.1.7
coverage==7.4.0
coverage==7.4.4
decorator==5.1.1
factory-boy==3.3.0
Faker==22.5.0
Faker==24.4.0
flasgger==0.9.5
Flask==2.2.2
Flask-Cors==3.0.10
Flask-JWT-Extended==4.2.2
flask-marshmallow==1.1.0
flask-marshmallow==1.2.1
Flask-Migrate==3.1.0
Flask-RESTful==0.3.10
Flask-SQLAlchemy==3.0.3
gevent==23.9.1
Flask-WTF==1.2.1
gevent==24.2.1
graphql-core==3.2.3
greenlet==3.0.3
gunicorn==20.1.0
Expand All @@ -32,54 +34,58 @@ itsdangerous==2.1.2
Jinja2==3.1.3
jsonschema==4.21.1
jsonschema-specifications==2023.12.1
libcst==1.1.0
libcst==1.2.0
loguru==0.6.0
Mako==1.3.1
Mako==1.3.2
markdown-it-py==3.0.0
MarkupSafe==2.1.4
marshmallow==3.20.2
marshmallow-sqlalchemy==0.30.0
MarkupSafe==2.1.5
marshmallow==3.21.1
marshmallow-sqlalchemy==1.0.0
mdurl==0.1.2
mistune==3.0.2
mypy-extensions==1.0.0
mysql-connector-python==8.0.29
packaging==23.2
packaging==24.0
pathspec==0.12.1
platformdirs==4.1.0
pluggy==1.3.0
protobuf==4.25.2
platformdirs==4.2.0
pluggy==1.4.0
protobuf==5.26.1
Pygments==2.17.2
PyJWT==2.8.0
pytest==7.4.4
pytest-cov==4.1.0
pytest-mock==3.12.0
pytest-socket==0.6.0
pytest-sugar==0.9.7
python-dateutil==2.8.2
pytest==8.1.1
pytest-cov==5.0.0
pytest-mock==3.14.0
pytest-socket==0.7.0
pytest-sugar==1.0.0
python-dateutil==2.9.0.post0
python-dotenv==0.20.0
python-multipart==0.0.6
pytz==2023.3.post1
python-multipart==0.0.9
pytz==2024.1
PyYAML==6.0.1
referencing==0.32.1
referencing==0.34.0
requests==2.31.0
rich==13.7.0
rpds-py==0.17.1
rich==13.7.1
rpds-py==0.18.0
secure==0.3.0
shellingham==1.5.4
six==1.16.0
sniffio==1.3.0
sniffio==1.3.1
soupsieve==2.5
SQLAlchemy==2.0.25
SQLAlchemy-serializer==1.4.1
starlette==0.36.1
strawberry-graphql==0.218.0
SQLAlchemy==2.0.29
SQLAlchemy-serializer==1.4.12
starlette==0.37.2
strawberry-graphql==0.224.1
termcolor==2.4.0
typer==0.9.0
typer==0.12.0
typer-cli==0.12.0
typer-slim==0.12.0
typing-inspect==0.9.0
typing_extensions==4.9.0
urllib3==2.1.0
uvicorn==0.27.0
typing_extensions==4.10.0
urllib3==2.2.1
uvicorn==0.29.0
validators==0.20.0
Werkzeug==2.2.2
WTForms==3.1.2
xmltodict==0.13.0
zope.event==5.0
zope.interface==6.1
zope.interface==6.2
84 changes: 38 additions & 46 deletions src/blueprints/auth.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
from flask import Blueprint, jsonify, request, Response, abort
from werkzeug.exceptions import HTTPException
from werkzeug.security import check_password_hash, generate_password_hash
import re
from typing import Tuple

import validators
from flasgger import swag_from
from flask import Blueprint, Response, abort, jsonify, request
from flask_jwt_extended import (
create_access_token,
create_refresh_token,
get_jwt_identity,
jwt_required,
)
from flasgger import swag_from
from werkzeug.exceptions import HTTPException
from werkzeug.security import check_password_hash, generate_password_hash

from src import db
from src.business_logic.favoris.delete_favoris_for_one_user import (
delete_favoris_for_one_user,
)
from src.business_logic.user.exceptions import AuthenticationException
from src.business_logic.user.validate_user import validate_user
from src.constants.http_status_codes import (
HTTP_200_OK,
HTTP_201_CREATED,
Expand All @@ -18,12 +27,7 @@
HTTP_404_NOT_FOUND,
HTTP_409_CONFLICT,
)
from src import db
from src.models import User, UserFavori

import re
from typing import Tuple

from src.models import User

auth = Blueprint("auth", __name__, url_prefix="/api/v1/auth")

Expand Down Expand Up @@ -85,38 +89,32 @@ def register() -> Tuple[Response, int]:
@auth.post("/login")
@swag_from("../docs/auth/login/login.yaml")
def login() -> Tuple[Response, int] | HTTPException:
# Collect informations
data = request.json
if data is not None:
try:
data = request.json
if data is None:
raise AuthenticationException("Invalid request body")

email = data.get("email")
password = data.get("password")

user = db.session.query(User).filter_by(email=email).first()

if user:
# Verify if password check with password in database
is_pass_correct = check_password_hash(user.password, password)

if is_pass_correct:
refresh = create_refresh_token(identity=user.id)
access = create_access_token(identity=user.id)

return (
jsonify(
{
"user": {
"id": user.id,
"refresh": refresh,
"access": access,
"username": user.username,
"email": user.email,
}
}
),
HTTP_200_OK,
)
return {"error": "Wrong credendials"}, HTTP_401_UNAUTHORIZED
return {"error": "Invalid request body"}, HTTP_400_BAD_REQUEST
user = validate_user(email, password)

refresh = create_refresh_token(identity=user.id)
access = create_access_token(identity=user.id)

response_data = {
"user": {
"id": user.id,
"refresh": refresh,
"access": access,
"username": user.username,
"email": user.email,
}
}

return jsonify(response_data), HTTP_200_OK
except AuthenticationException as e:
return jsonify({"error": str(e)}), HTTP_401_UNAUTHORIZED


# Me function need JWT token return userInfo
Expand Down Expand Up @@ -224,12 +222,6 @@ def edit_user() -> Tuple[Response, int] | HTTPException:
# Remove_user funtion need JWT token and remove user in database


def remove_favoris(user_id: int) -> None:
favoris = db.session.query(UserFavori).filter(UserFavori.user_id == user_id).all()
[db.session.delete(f) for f in favoris]
db.session.commit()


@auth.delete("/me/remove")
@jwt_required()
@swag_from("../docs/auth/remove.yaml")
Expand All @@ -240,7 +232,7 @@ def remove_user() -> Tuple[Response, int] | HTTPException:
if not user:
abort(HTTP_404_NOT_FOUND, "User not found")

remove_favoris(user_id)
delete_favoris_for_one_user(user_id)

db.session.delete(user)
db.session.commit()
Expand Down
23 changes: 23 additions & 0 deletions src/blueprints/legal/templates/delete_me.html.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% extends "base.html.jinja" %} {% block topcontainer %}
<h2>Delete your account</h2>
<form method="post" action="/me/delete">
{{ form.csrf_token }}
{{ form.hidden_tag() }}
<div>
{{ form.email.label }}<br>
{{ form.email }}
{% for error in form.email.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.password.label }}<br>
{{ form.password }}
{% for error in form.password.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
<br>
<input type="submit" value="Submit">
</form>
{%endblock%}
4 changes: 4 additions & 0 deletions src/blueprints/legal/templates/delete_successfully.html.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "base.html.jinja" %} {% block topcontainer %}
<h2>Bye 👋</h2>
<p>{{ name }}. Your has been successfully deleted.</p>
{%endblock%}
4 changes: 4 additions & 0 deletions src/blueprints/legal/templates/error.html.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "base.html.jinja" %} {% block topcontainer %}
<h2>Oupss!</h2>
<p>Somethings wrong : {{ error }}</p>
{%endblock%}
36 changes: 36 additions & 0 deletions src/blueprints/legal/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
from flask import Blueprint, render_template
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import InputRequired

from src import db
from src.business_logic.favoris.delete_favoris_for_one_user import (
delete_favoris_for_one_user,
)
from src.business_logic.user.exceptions import AuthenticationException
from src.business_logic.user.validate_user import validate_user


class DeleteUserForm(FlaskForm):
email = StringField("Email", validators=[InputRequired()])
password = StringField("Password", validators=[InputRequired()])


legal = Blueprint("legal", __name__, template_folder="templates")
Expand All @@ -7,3 +22,24 @@
@legal.route("/privacy_policy")
def privacy_policy():
return render_template("privacy_policy.html.jinja")


@legal.route("/me/delete", methods=["GET", "POST"])
def delete_my_account():
form = DeleteUserForm()
try:
if form.validate_on_submit():
email = form.email.data
password = form.password.data

user = validate_user(email, password)

delete_favoris_for_one_user(user.id)

db.session.delete(user)
db.session.commit()

return render_template("delete_successfully.html.jinja", name=email)
except AuthenticationException as e:
return render_template("error.html.jinja", error=str(e))
return render_template("delete_me.html.jinja", form=form)
Loading

0 comments on commit bb007a3

Please sign in to comment.