From 3368f2d2d3f025cfef29a5682478440bd5b5e766 Mon Sep 17 00:00:00 2001 From: Lorenzo Balugani Date: Thu, 20 May 2021 13:48:15 +0200 Subject: [PATCH] Add authorization creation/deletion API calls --- nest_backend/api_schemas.py | 14 +++ nest_backend/api_spec.py | 2 + nest_backend/app.py | 10 ++ nest_backend/database/tables/Authorization.py | 8 +- nest_backend/errors.py | 2 + nest_backend/gestione.py | 6 +- nest_backend/routes/__init__.py | 4 +- nest_backend/routes/repository/__init__.py | 4 +- .../repository/authorizations/__init__.py | 4 + .../authorizations/authorization.py | 54 ++++++++++ .../repository_authorizations.py | 101 ++++++++++++++++++ nest_backend/routes/repository/repository.py | 1 + 12 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 nest_backend/routes/repository/authorizations/__init__.py create mode 100644 nest_backend/routes/repository/authorizations/authorization.py create mode 100644 nest_backend/routes/repository/authorizations/repository_authorizations.py diff --git a/nest_backend/api_schemas.py b/nest_backend/api_schemas.py index dd70f7a..47894d3 100644 --- a/nest_backend/api_schemas.py +++ b/nest_backend/api_schemas.py @@ -24,6 +24,20 @@ class ErrorSchema(Schema): code = fields.String(description="Error code") +class AuthorizationSchema(Schema): + rid = fields.Integer(description="The repository id.") + email = fields.String(description="The user's email.") + + +class AuthorizationParameterSchema(Schema): + rid = fields.Integer(description="The repository id.") + email = fields.String(description="The user's email.") + + +class CreateAuthorizationSchema(Schema): + email = fields.String(description="The user's email") + + class SuccesSchema(Schema): result = fields.String(description="Contains a string that informs if the procedure was successful.") data = fields.String(description="The content of the response.") diff --git a/nest_backend/api_spec.py b/nest_backend/api_spec.py index beda678..366abfd 100644 --- a/nest_backend/api_spec.py +++ b/nest_backend/api_spec.py @@ -33,6 +33,8 @@ spec.components.schema("AlertParameter", schema=AlertParameterSchema) spec.components.schema("Alert", schema=Alert) spec.components.schema("Tweet", schema=TweetSchema) spec.components.schema("CreateAlert", schema=CreateAlert) +spec.components.schema("CreateAuthorization", schema=CreateAuthorizationSchema) +spec.components.schema("Authorization", schema=AuthorizationSchema) spec.components.security_scheme("jwt", {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"}) # add swagger tags that are used for endpoint annotation diff --git a/nest_backend/app.py b/nest_backend/app.py index 5fbf025..4f7b656 100644 --- a/nest_backend/app.py +++ b/nest_backend/app.py @@ -66,11 +66,21 @@ app.add_url_rule( view_func=routes.page_repository_alerts, methods=["GET", "POST"] ) +app.add_url_rule( + "/api/v1/repositories//authorizations/", + view_func=routes.page_repository_authorizations, + methods=["GET", "POST"], +) app.add_url_rule( "/api/v1/repositories//tweets/", view_func=routes.page_repository_tweets, methods=["GET"] ) +app.add_url_rule( + "/api/v1/authorization//", + view_func=routes.page_authorization, + methods=["DELETE"] +) app.add_url_rule( "/api/v1/alert/", view_func=routes.page_alert, diff --git a/nest_backend/database/tables/Authorization.py b/nest_backend/database/tables/Authorization.py index 978aabe..7018bb5 100644 --- a/nest_backend/database/tables/Authorization.py +++ b/nest_backend/database/tables/Authorization.py @@ -10,4 +10,10 @@ class Authorization(ext.Model): email = ext.Column(ext.String, ext.ForeignKey("user.email", ondelete="CASCADE"), primary_key=True) # Relationships repository = ext.relationship("Repository", back_populates="authorizations") - user = ext.relationship("User", back_populates="authorizations") \ No newline at end of file + user = ext.relationship("User", back_populates="authorizations") + + def to_json(self): + return { + "rid": self.rid, + "email": self.email, + } \ No newline at end of file diff --git a/nest_backend/errors.py b/nest_backend/errors.py index 6fd48f7..cdd0004 100644 --- a/nest_backend/errors.py +++ b/nest_backend/errors.py @@ -25,3 +25,5 @@ ALERT_NO_LIMIT = "errorAlertNoLimit" # Missing limit entry ALERT_NO_WINDOW = "errorAlertNoWindow" # Missing window entry ALERT_NO_EVALUATION = "errorAlertNoEvaluation" # Missing evalmode entry ALERT_DELETION_FAILURE = "errorAlertDeletionFailure" # Error while deleting alerts +# Authorization +AUTHORIZATION_NOT_FOUND = "errorAuthorizationNotFound" # Authorization not found diff --git a/nest_backend/gestione.py b/nest_backend/gestione.py index 29462b5..2345b0b 100644 --- a/nest_backend/gestione.py +++ b/nest_backend/gestione.py @@ -8,7 +8,7 @@ import functools from flask_jwt_extended import get_jwt_identity from flask import jsonify from re import sub -from .errors import GENERIC_UFO +from .errors import GENERIC_UFO, REPOSITORY_NOT_FOUND, USER_NOT_AUTHORIZED __all__ = ["authenticate", "identity", "gen_password", "find_user", "admin_or_403", "repository_auth", "json_request_authorizer", "json_error", @@ -76,10 +76,10 @@ def repository_auth(f): return json_error("Missing one or more parameters."), 400 repository = Repository.query.filter_by(id=repository_id).first() if not repository: - return json_error("Cant't find the repository."), 404 + return json_error("Cant't find the repository.", REPOSITORY_NOT_FOUND), 404 if repository.owner_id != user.email and user.email not in [a.email for a in repository.authorizations] and not user.isAdmin: - return json_error("Stop right there, criminal scum! Nobody accesses protected data under MY watch!"), 403 + return json_error("Stop right there, criminal scum! Nobody accesses protected data under MY watch!", USER_NOT_AUTHORIZED), 403 return f(*args, **kwargs) return func diff --git a/nest_backend/routes/__init__.py b/nest_backend/routes/__init__.py index b5db332..d9ac9a7 100644 --- a/nest_backend/routes/__init__.py +++ b/nest_backend/routes/__init__.py @@ -5,6 +5,8 @@ This module imports all the routes that return something to the frontend. from .doa import page_doa from .users import * from .repository import * + __all__ = ["page_alert", "page_repository_alerts", "page_repository", "page_doa", "page_condition", "page_repository_conditions", "page_repositories", - "page_login", "page_user", "page_users"] \ No newline at end of file + "page_login", "page_user", "page_users", "page_authorization", "page_repository_authorizations", + "page_repository_tweets"] diff --git a/nest_backend/routes/repository/__init__.py b/nest_backend/routes/repository/__init__.py index 44dc306..1b195c8 100644 --- a/nest_backend/routes/repository/__init__.py +++ b/nest_backend/routes/repository/__init__.py @@ -4,6 +4,8 @@ from .repositories import page_repositories from .conditions import * from .alerts import * from .tweets import * +from .authorizations import * __all__ = ["page_condition", "page_repository_conditions", "page_repositories", - "page_alert", "page_repository", "page_repository_alerts", "page_repository_tweets"] + "page_alert", "page_repository", "page_repository_alerts", "page_repository_tweets", + "page_authorization", "page_repository_authorizations"] diff --git a/nest_backend/routes/repository/authorizations/__init__.py b/nest_backend/routes/repository/authorizations/__init__.py new file mode 100644 index 0000000..d60d00e --- /dev/null +++ b/nest_backend/routes/repository/authorizations/__init__.py @@ -0,0 +1,4 @@ +from .authorization import page_authorization +from .repository_authorizations import page_repository_authorizations + +__all__ = ["page_authorization", "page_repository_authorizations"] diff --git a/nest_backend/routes/repository/authorizations/authorization.py b/nest_backend/routes/repository/authorizations/authorization.py new file mode 100644 index 0000000..94917f7 --- /dev/null +++ b/nest_backend/routes/repository/authorizations/authorization.py @@ -0,0 +1,54 @@ +from flask import render_template, abort, jsonify, request +from nest_backend.database import * +from flask_jwt_extended import jwt_required, get_jwt_identity +from nest_backend.gestione import * +from flask_cors import cross_origin +from nest_backend.errors import * + + +@cross_origin() +@jwt_required() +def page_authorization(rid, email): + """ + --- + delete: + summary: Delete an authorization. + parameters: + - in: path + schema: AuthorizationParameterSchema + security: + - jwt: [] + responses: + '204': + description: The deletion was successful. + '401': + description: The user is not logged in. + content: + application/json: + schema: Error + '403': + description: The user is not authorized. + content: + application/json: + schema: Error + '404': + description: Something could not be found. + content: + application/json: + schema: Error + tags: + - repository-related + """ + repository = Repository.query.filter_by(id=rid).first() + user = find_user(get_jwt_identity()) + if not repository: + return json_error("Could not find the repository.", REPOSITORY_NOT_FOUND), 404 + if user != repository.owner: + return json_error("You are not authorized.", USER_NOT_AUTHORIZED), 403 + authorization = Authorization.query.filter_by(rid=rid, email=email).first() + if not authorization: + return json_error("Could not find the authorization", AUTHORIZATION_NOT_FOUND), 404 + if request.method == "DELETE": + ext.session.delete(authorization) + ext.session.commit() + return json_success("Deleted."), 204 diff --git a/nest_backend/routes/repository/authorizations/repository_authorizations.py b/nest_backend/routes/repository/authorizations/repository_authorizations.py new file mode 100644 index 0000000..3a32421 --- /dev/null +++ b/nest_backend/routes/repository/authorizations/repository_authorizations.py @@ -0,0 +1,101 @@ +from flask import request +from flask_jwt_extended import jwt_required +from nest_backend.gestione import repository_auth, json_error, json_success, find_user, get_jwt_identity +from nest_backend.database import ext, User, Authorization, Repository +from flask_cors import cross_origin +from nest_backend.gestione import hashtag_validator +from nest_backend.errors import * + + +@cross_origin() +@jwt_required() +@repository_auth +def page_repository_authorizations(rid): + """ + --- + get: + summary: Get a list of a repository authorizations. + parameters: + - in: path + schema: IntegerParameterSchema + security: + - jwt: [] + responses: + '200': + description: List of Authorization schemas, incapsulated in Success. + '401': + description: The user is not logged in. + content: + application/json: + schema: Error + '403': + description: The user is not authorized. + content: + application/json: + schema: Error + '404': + description: The repository could not be found. + content: + application/json: + schema: Error + tags: + - repository-related + post: + summary: Creates a condition and attaches it to the repository. + security: + - jwt: [] + parameters: + - in: path + schema: IntegerParameterSchema + requestBody: + required: true + content: + application/json: + schema: CreateAuthorization + responses: + '201': + description: The authorization has been created successfully. + content: + application/json: + schema: Authorization + '403': + description: The user is not authorized. + content: + application/json: + schema: Error + '401': + description: The user is not logged in. + content: + application/json: + schema: Error + tags: + - repository-related + """ + + repository = Repository.query.filter_by(id=rid).first() + if not repository: + return json_error("Could not find repository", REPOSITORY_NOT_FOUND), 404 + user = find_user(get_jwt_identity()) + if user.email != repository.owner_id: + return json_error("You are not authorized.", REPOSITORY_NOT_OWNER), 403 + if request.method == "GET": + try: + return json_success([a.to_json() for a in repository.authorizations]) + except Exception as e: + return json_error("Unknown error:" + str(e), GENERIC_UFO), 400 + if request.method == "POST": + if request.json is None: + return json_error("Missing json content.", GENERIC_NO_JSON), 400 + if not request.json.get("email"): + return json_error("Missing user email.", GENERIC_MISSING_FIELDS), 400 + target = User.query.filter_by(email=request.json.get('email')).first() + if not target: + return json_error("User could not be located", USER_NOT_FOUND), 400 + if target == user: + return json_error("Owner cannot be a spectator", GENERIC_ALREADY_EXISTS), 406 + + authorization = Authorization(email=request.json.get('email'), rid=repository.id) + ext.session.add(authorization) + ext.session.commit() + + return json_success(authorization.to_json()), 201 diff --git a/nest_backend/routes/repository/repository.py b/nest_backend/routes/repository/repository.py index cb1e954..a9907b0 100644 --- a/nest_backend/routes/repository/repository.py +++ b/nest_backend/routes/repository/repository.py @@ -198,6 +198,7 @@ def page_repository(rid): repository.evaluation_mode = evaluation_mode repository.name = request.json['name'] repository.is_active = request.json['is_active'] + ext.session.commit() ids = [c['id'] for c in request.json['conditions'] if c['id']] # Delete no longer needed conditions. for c in repository.conditions: