diff --git a/code/backend/nest_backend/__main__.py b/code/backend/nest_backend/__main__.py index 57839b5..bb8b1e1 100644 --- a/code/backend/nest_backend/__main__.py +++ b/code/backend/nest_backend/__main__.py @@ -8,6 +8,7 @@ from .routes import * from .database import Base, tables import psycopg2 from .gestione import * +from flask_cors import CORS from flask_jwt_extended import * app = Flask(__name__) @@ -25,6 +26,8 @@ app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:password@localhos Base.app = app Base.init_app(app) jwt = JWTManager(app) +cors = CORS(app) +app.config['CORS_HEADERS'] = 'Content-Type' # Routes setup app.add_url_rule("/doa", view_func=page_doa, methods=["GET", "POST"]) diff --git a/code/backend/nest_backend/database/tables/Repository.py b/code/backend/nest_backend/database/tables/Repository.py index 27dfe8a..d01c1ed 100644 --- a/code/backend/nest_backend/database/tables/Repository.py +++ b/code/backend/nest_backend/database/tables/Repository.py @@ -10,7 +10,7 @@ class Repository(Base.Model): id = Base.Column(Base.Integer, primary_key=True) name = Base.Column(Base.String, nullable=False) start = Base.Column(Base.DateTime, nullable=False) - end = Base.Column(Base.DateTime, nullable=False) + end = Base.Column(Base.DateTime, nullable=True) # Foreign Keys owner_id = Base.Column(Base.String, Base.ForeignKey("user.email"), nullable=False) # Relationships @@ -18,4 +18,7 @@ class Repository(Base.Model): authorizations = Base.relationship("Authorization", back_populates="repository") tweets = Base.relationship("Composed", back_populates="repository") alerts = Base.relationship("Alert", back_populates="repository") - uses = Base.relationship("Uses", back_populates="repository") \ No newline at end of file + uses = Base.relationship("Uses", back_populates="repository") + + def to_json(self): + return {"id": self.id, "name": self.name, "start": self.start.isoformat(), "owner": self.owner.to_json()} diff --git a/code/backend/nest_backend/database/tables/User.py b/code/backend/nest_backend/database/tables/User.py index f54f0bf..b59e2a9 100644 --- a/code/backend/nest_backend/database/tables/User.py +++ b/code/backend/nest_backend/database/tables/User.py @@ -13,4 +13,7 @@ class User(Base.Model): isAdmin = Base.Column(Base.Boolean, default=False) # Relationships owner_of = Base.relationship("Repository", back_populates="owner") - authorizations = Base.relationship("Authorization", back_populates="user") \ No newline at end of file + authorizations = Base.relationship("Authorization", back_populates="user") + + def to_json(self): + return {'email': self.email, 'username': self.username, 'isAdmin': self.isAdmin} diff --git a/code/backend/nest_backend/gestione.py b/code/backend/nest_backend/gestione.py index 72a5242..170d630 100644 --- a/code/backend/nest_backend/gestione.py +++ b/code/backend/nest_backend/gestione.py @@ -60,4 +60,4 @@ def admin_or_403(f): current_user = get_jwt_identity() return f(*args, **kwargs) - return func + return func \ No newline at end of file diff --git a/code/backend/nest_backend/routes/repository/__init__.py b/code/backend/nest_backend/routes/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/code/backend/nest_backend/routes/repository/repository_create.py b/code/backend/nest_backend/routes/repository/repository_create.py new file mode 100644 index 0000000..c96264b --- /dev/null +++ b/code/backend/nest_backend/routes/repository/repository_create.py @@ -0,0 +1,24 @@ +from flask import render_template, abort, jsonify, request +from ...database import * +from flask_jwt_extended import jwt_required +from ...gestione import * +import datetime +from flask_cors import cross_origin + + +@cross_origin() +@jwt_required() +def page_repository_create(): + """ + API call that allows an user to create a new repository. + :form name: The name of the repository. + :returns: If the user is logged in and + """ + user = find_user(get_jwt_identity()) + name = request.json.get("name") + if not name: + return jsonify({"result": "failure", "msg": "Missing one or more parameters"}), 40 + repository = Repository(name=name, start=datetime.datetime.now(), owner_id=user.email) + Base.session.add(repository) + Base.session.commit() + return jsonify({"result":"success", "content":repository.to_json()}), 200 \ No newline at end of file diff --git a/code/backend/nest_backend/routes/users/__init__.py b/code/backend/nest_backend/routes/users/__init__.py index f83b7ce..df8755e 100644 --- a/code/backend/nest_backend/routes/users/__init__.py +++ b/code/backend/nest_backend/routes/users/__init__.py @@ -1,2 +1,3 @@ from .user_create import page_user_create -from .login import page_login \ No newline at end of file +from .login import page_login +from .user_delete import page_user_delete \ No newline at end of file diff --git a/code/backend/nest_backend/routes/users/login.py b/code/backend/nest_backend/routes/users/login.py index 3948521..407e23c 100644 --- a/code/backend/nest_backend/routes/users/login.py +++ b/code/backend/nest_backend/routes/users/login.py @@ -2,14 +2,16 @@ from flask import render_template, abort, jsonify, request from ...database import * from ...gestione import * from flask_jwt_extended import create_access_token +from flask_cors import cross_origin +@cross_origin() def page_login(): """ The API call that allows to log-in. It requires: :form email: The user's email :form password: The users's password - :return: Json-formatted data. If the login is successful, it will contain the access_token. + :return: Json-formatted data. If the login is successful, it will contain the access_token and the users data. The access_token must be included in the Authorization header, using the format Bearer . """ @@ -17,5 +19,6 @@ def page_login(): password = request.json.get("password", None) if authenticate(email, password): access_token = create_access_token(identity=email) - return jsonify({"result": "success", "access_token": access_token}), 201 + user = find_user(email) + return jsonify({"result": "success", "access_token": access_token, 'user': user.to_json()}), 201 return jsonify({"result": "failure", "msg": "Bad username or password."}), 401 diff --git a/code/backend/nest_backend/routes/users/user_create.py b/code/backend/nest_backend/routes/users/user_create.py index bf98b3f..dc831bf 100644 --- a/code/backend/nest_backend/routes/users/user_create.py +++ b/code/backend/nest_backend/routes/users/user_create.py @@ -2,7 +2,10 @@ from flask import render_template, abort, jsonify, request from ...database import * from flask_jwt_extended import jwt_required from ...gestione import * +from flask_cors import cross_origin + +@cross_origin() @jwt_required() def page_user_create(): """ @@ -11,12 +14,13 @@ def page_user_create(): :form password: The users's password :form username: The users's username :return: Json-formatted data. If something goes wrong, it returns a - {'result':'failure', 'content':'something blew up'}, else it returns {'result':'success', 'content':{newUser.to_json()}. + {'result':'failure', 'content':'something blew up'}, else it returns {'result':'success', 'content':newUser.to_json(). """ user = find_user(get_jwt_identity()) if not user.isAdmin: abort(403) - nUser = User(email=request.json.get("email"), password=gen_password(request.json.get("password")), username=request.json.get("username")) + nUser = User(email=request.json.get("email"), password=gen_password(request.json.get("password")), + username=request.json.get("username")) Base.session.add(nUser) Base.session.commit() - return jsonify({"result":"success", "content":"something"}) + return jsonify({"result": "success", "content": user.to_json()}) diff --git a/code/backend/nest_backend/routes/users/user_delete.py b/code/backend/nest_backend/routes/users/user_delete.py new file mode 100644 index 0000000..3e30e08 --- /dev/null +++ b/code/backend/nest_backend/routes/users/user_delete.py @@ -0,0 +1,27 @@ +from flask import render_template, abort, jsonify, request +from ...database import * +from flask_jwt_extended import jwt_required +from ...gestione import * +from flask_cors import cross_origin + + +@cross_origin() +@jwt_required() +def page_user_delete(): + """ + API call that allows an user to be deleted from NEST. + :form email: The email of the user that needs to be removed. + :return: JSON-formatted data. If the user has the privilege, the target user exists and differs from the current + user, the target gets deleted and a json containing the field "result":"success" is returned. + """ + user = find_user(get_jwt_identity()) + if not user.isAdmin: + return jsonify({"result": "failure", "msg": "User is not admin."}), 403 + target = find_user(request.json.get('email')) + if not target: + return jsonify({"result": "failure", "msg": "User not found."}), 404 + if user == target: + return jsonify({"result": "failure", "msg": "The user cant delete himself. Its a sin."}), 406 + Base.session.remove(target) + Base.session.commit() + return jsonify({"result": "success", "content":"The user has been deleted."}) diff --git a/code/backend/poetry.lock b/code/backend/poetry.lock index a81a551..78ee92e 100644 --- a/code/backend/poetry.lock +++ b/code/backend/poetry.lock @@ -52,6 +52,18 @@ dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxco docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] dotenv = ["python-dotenv"] +[[package]] +name = "flask-cors" +version = "3.0.10" +description = "A Flask extension adding a decorator for CORS support" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +Flask = ">=0.9" +Six = "*" + [[package]] name = "flask-jwt-extended" version = "4.1.0" @@ -205,7 +217,7 @@ watchdog = ["watchdog"] [metadata] lock-version = "1.1" python-versions = "^3.8.5" -content-hash = "6bc937df83a15e8774d6ab63f19f5b6b3780730d935678c81259f53ee7cd8ebc" +content-hash = "8269162351f5e7827601a662d25048bbc0f58503fa4d251334c45b29feac5e5c" [metadata.files] bcrypt = [ @@ -264,6 +276,10 @@ flask = [ {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"}, {file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"}, ] +flask-cors = [ + {file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"}, + {file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"}, +] flask-jwt-extended = [ {file = "Flask-JWT-Extended-4.1.0.tar.gz", hash = "sha256:77ca23f23e80480ea42b9c1d9b0fca214e08db7192583e782c2421416b4a4655"}, {file = "Flask_JWT_Extended-4.1.0-py2.py3-none-any.whl", hash = "sha256:f952f4ebd449182431c6755acfb7cbb52b8034df7a9f9ef95eb51ccfc1e235b0"}, diff --git a/code/backend/pyproject.toml b/code/backend/pyproject.toml index e6f1ace..2c77c25 100644 --- a/code/backend/pyproject.toml +++ b/code/backend/pyproject.toml @@ -11,6 +11,7 @@ Flask = "^1.1.2" Flask-SQLAlchemy = "^2.5.1" bcrypt = "^3.2.0" Flask-JWT-Extended = "^4.1.0" +Flask-Cors = "^3.0.10" [tool.poetry.dev-dependencies]