From bcf5666af63dd1fee17270df2e76600121bb301c Mon Sep 17 00:00:00 2001 From: Peter Yefi Date: Tue, 10 Jan 2023 17:24:26 -0500 Subject: [PATCH 1/6] Added user management API to add and update user --- bootstrap.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++ gamification.py | 80 ++++--------------------------------------------- hub_api/user.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 74 deletions(-) create mode 100644 bootstrap.py create mode 100644 hub_api/user.py diff --git a/bootstrap.py b/bootstrap.py new file mode 100644 index 0000000..4ea664b --- /dev/null +++ b/bootstrap.py @@ -0,0 +1,77 @@ +""" +Main +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author name guillermo.gutierrezmorote@concordia.ca +Project Collaborator name peteryefi@gmail.com +""" + +import flask +from apispec import APISpec +from apispec.ext.marshmallow import MarshmallowPlugin +from flask_apispec.extension import FlaskApiSpec +from flask_restful import Api +from hub_api.city_info import CityInfo +from hub_api.geometry import Geometry +from hub_api.greenery_catalog import GreeneryCatalogEntries +from hub_api.greenery_catalog import GreeneryCatalogEntry +from hub_api.greenery_catalog import GreeneryCatalogNames +from hub_api.construction_catalog import ConstructionCatalogEntries +from hub_api.construction_catalog import ConstructionCatalogEntry +from hub_api.construction_catalog import ConstructionCatalogNames +from hub_api.usage_catalog import UsageCatalogEntries +from hub_api.usage_catalog import UsageCatalogEntry +from hub_api.usage_catalog import UsageCatalogNames +from hub_api.heat_pump import HeatPump +from hub_api.lca import MaterialLCACatalog +from hub_api.lca import MaterialLCACalculations +from hub_api.construction import Construction +from hub_api.usage import Usage +from hub_api.energy_demand import EnergyDemand +from hub_api.session import SessionStart, SessionEnd, KeepSessionAlive +from hub_api.uptime import Uptime +from hub_api.greenery import Greenery +from hub_api.user import User + +app = flask.Flask('gamification') +api = Api(app) + + +api.add_resource(Uptime, '/v1.4/uptime') +api.add_resource(Geometry, '/v1.4/geometry') +api.add_resource(GreeneryCatalogEntry, '/v1.4/greenery-catalog/entry') +api.add_resource(GreeneryCatalogEntries, '/v1.4/greenery-catalog/entries') +api.add_resource(GreeneryCatalogNames, '/v1.4/greenery-catalog/names') +api.add_resource(ConstructionCatalogEntry, '/v1.4/construction-catalog/entry') +api.add_resource(ConstructionCatalogEntries, '/v1.4/construction-catalog/entries') +api.add_resource(ConstructionCatalogNames, '/v1.4/construction-catalog/names') +api.add_resource(Construction, '/v1.4/construction') +api.add_resource(UsageCatalogEntry, '/v1.4/usage-catalog/entry') +api.add_resource(UsageCatalogEntries, '/v1.4/usage-catalog/entries') +api.add_resource(UsageCatalogNames, '/v1.4/usage-catalog/names') +api.add_resource(Usage, '/v1.4/usage') +api.add_resource(EnergyDemand, '/v1.4/energy-demand') +# api.add_resource(LCA, '/v1.4/lca') +api.add_resource(MaterialLCACatalog, '/v1.4/material_lca_catalog/entries') +api.add_resource(MaterialLCACalculations, '/v1.4/material_lca_catalog/calculations') +api.add_resource(HeatPump, '/v1.4/heat-pump') +api.add_resource(User, '/v1.4/user') +api.add_resource(SessionStart, '/v1.4/session/start') +api.add_resource(SessionEnd, '/v1.4/session/end') +api.add_resource(KeepSessionAlive, '/v1.4/session/keep_alive') +api.add_resource(CityInfo, '/v1.4/city_info') +api.add_resource(Greenery, '/v1.4/greenery') + +# Add api documentation +app.config.update({ + 'APISPEC_SPEC': APISpec( + title='Gamification Service', + version='v1.4', + plugins=[MarshmallowPlugin()], + openapi_version='2.0.0' + ), + 'APISPEC_SWAGGER_URL': '/swagger/', # URI to access API Doc JSON + 'APISPEC_SWAGGER_UI_URL': '/swagger-ui/' # URI to access UI of API Doc +}) +docs = FlaskApiSpec(app) +docs.register(HeatPump) +docs.register(User) diff --git a/gamification.py b/gamification.py index 6793dcc..7e41b5a 100644 --- a/gamification.py +++ b/gamification.py @@ -2,18 +2,9 @@ Main SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2021 Project Author name guillermo.gutierrezmorote@concordia.ca +Project Collaborator name peteryefi@gmail.com """ -import datetime -from pathlib import Path - -import flask -from apispec import APISpec -from apispec.ext.marshmallow import MarshmallowPlugin -from flask import Response -from flask_apispec.extension import FlaskApiSpec -from flask_restful import Api - from catalog_factories.construction_catalog_factory import ConstructionCatalogFactory from catalog_factories.greenery_catalog_factory import GreeneryCatalogFactory from catalog_factories.usage_catalog_factory import UsageCatalogFactory @@ -23,69 +14,11 @@ from imports.life_cycle_assessment_factory import LifeCycleAssessment from imports.schedules_factory import SchedulesFactory from imports.usage_factory import UsageFactory from imports.weather_factory import WeatherFactory -from hub_api.city_info import CityInfo -from hub_api.geometry import Geometry -from hub_api.greenery_catalog import GreeneryCatalogEntries -from hub_api.greenery_catalog import GreeneryCatalogEntry -from hub_api.greenery_catalog import GreeneryCatalogNames -from hub_api.construction_catalog import ConstructionCatalogEntries -from hub_api.construction_catalog import ConstructionCatalogEntry -from hub_api.construction_catalog import ConstructionCatalogNames -from hub_api.usage_catalog import UsageCatalogEntries -from hub_api.usage_catalog import UsageCatalogEntry -from hub_api.usage_catalog import UsageCatalogNames -from hub_api.heat_pump import HeatPump -from hub_api.lca import MaterialLCACatalog -from hub_api.lca import MaterialLCACalculations -from hub_api.construction import Construction -from hub_api.usage import Usage -from hub_api.energy_demand import EnergyDemand -from hub_api.session import SessionStart, SessionEnd, KeepSessionAlive -from hub_api.uptime import Uptime -from hub_api.greenery import Greenery +from flask import Response import hub_api.helpers.session_helper as sh - -app = flask.Flask('gamification') -api = Api(app) - - -api.add_resource(Uptime, '/v1.3/uptime') -api.add_resource(Geometry, '/v1.3/geometry') -api.add_resource(GreeneryCatalogEntry, '/v1.3/greenery-catalog/entry') -api.add_resource(GreeneryCatalogEntries, '/v1.3/greenery-catalog/entries') -api.add_resource(GreeneryCatalogNames, '/v1.3/greenery-catalog/names') -api.add_resource(ConstructionCatalogEntry, '/v1.3/construction-catalog/entry') -api.add_resource(ConstructionCatalogEntries, '/v1.3/construction-catalog/entries') -api.add_resource(ConstructionCatalogNames, '/v1.3/construction-catalog/names') -api.add_resource(Construction, '/v1.3/construction') -api.add_resource(UsageCatalogEntry, '/v1.3/usage-catalog/entry') -api.add_resource(UsageCatalogEntries, '/v1.3/usage-catalog/entries') -api.add_resource(UsageCatalogNames, '/v1.3/usage-catalog/names') -api.add_resource(Usage, '/v1.3/usage') -api.add_resource(EnergyDemand, '/v1.3/energy-demand') -# api.add_resource(LCA, '/v1.3/lca') -api.add_resource(MaterialLCACatalog, '/v1.3/material_lca_catalog/entries') -api.add_resource(MaterialLCACalculations, '/v1.3/material_lca_catalog/calculations') -api.add_resource(HeatPump, '/v1.3/heat-pump') -api.add_resource(SessionStart, '/v1.3/session/start') -api.add_resource(SessionEnd, '/v1.3/session/end') -api.add_resource(KeepSessionAlive, '/v1.3/session/keep_alive') -api.add_resource(CityInfo, '/v1.3/city_info') -api.add_resource(Greenery, '/v1.3/greenery') - -# Add api documentation -app.config.update({ - 'APISPEC_SPEC': APISpec( - title='Gamification Service', - version='v1.3', - plugins=[MarshmallowPlugin()], - openapi_version='2.0.0' - ), - 'APISPEC_SWAGGER_URL': '/swagger/', # URI to access API Doc JSON - 'APISPEC_SWAGGER_UI_URL': '/swagger-ui/' # URI to access UI of API Doc -}) -docs = FlaskApiSpec(app) -docs.register(HeatPump) +import datetime +from pathlib import Path +from bootstrap import app sh.begin_time = datetime.datetime.now() # initialize catalogs @@ -93,13 +26,11 @@ sh.greenery_catalog = GreeneryCatalogFactory('nrel').catalog sh.construction_catalog = ConstructionCatalogFactory('nrel').catalog sh.usage_catalog = UsageCatalogFactory('comnet').catalog - # Enrich the city data_path = (Path(__file__).parent / 'data').resolve() rihno_path = (Path(data_path / 'dompark.3dm')).resolve() city = GeometryFactory('rhino', rihno_path).city - for building in city.buildings: # Rihno files have no information about the function or the year of construction building.year_of_construction = 1995 @@ -135,6 +66,7 @@ for building in city.buildings: # Pass the city to the session helper to be used as default status. sh.city = city + @app.route("/") def home(): return Response(headers={'Access-Control-Allow-Origin': '*'}) diff --git a/hub_api/user.py b/hub_api/user.py new file mode 100644 index 0000000..61ddfb5 --- /dev/null +++ b/hub_api/user.py @@ -0,0 +1,75 @@ +""" +HeatPump Service +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Project Author Peter Yefi peteryefi@gmail.com +""" +import json + +from flask import Response +from flask_apispec import use_kwargs, doc +from flask_apispec.views import MethodResource +from flask_restful import Resource +from marshmallow import Schema, fields +from imports.user_factory import UserFactory +import os +from hub_logger import logger + + +class UserPostData(Schema): + """ + Defines post data for users + """ + name = fields.String(required=True, description='Name of user') + email = fields.String(required=True, description='Email of user') + password = fields.String(required=True, description='Password of user') + role = fields.String(required=True, description='Allowed user roles', enum=['Admin', 'Hub_Reader']) + + +class UserPutData(UserPostData): + """ + Defines put data for users + """ + id = fields.Int(required=True, description='The Id of the user to be Updated') + + +class UserLoginData(Schema): + """ + Defines post data for users + """ + email = fields.String(required=True, description='Email of user') + password = fields.String(required=True, description='Password of user') + + +class User(MethodResource, Resource): + def __init__(self): + self.user_factory = UserFactory(db_name='hub_prod', app_env='PROD', + dotenv_path="{}/.env".format(os.path.expanduser('~'))) + + @doc(description='Create users', tags=['CreateUser']) + @use_kwargs(UserPostData) + def post(self, **kwargs): + try: + + user = self.user_factory.create_user(name=kwargs["name"], email=kwargs["email"], password=kwargs["password"], + role=kwargs["role"]) + if type(user) is dict: + return Response(response=json.dumps(user), status=200) + return Response(response=json.dumps({'user': {'id': user.id, 'name': user.name, 'email': user.email, + 'password': user.password, 'role': user.role.value}}), status=201) + except Exception as err: + logger.error(err) + return Response(response=json.dumps({'err_msg': 'Sorry an error occurred while creating user'}), status=400) + + @doc(description='Get all users', tags=['UpdateUsers']) + @use_kwargs(UserPutData) + def put(self, **kwargs): + try: + res = self.user_factory.update_user(user_id=kwargs['id'], name=kwargs['name'], password=kwargs['password'], + role=kwargs['role'], email=kwargs['email']) + if res: + return Response(response=json.dumps(res), status=400) + return Response(response=json.dumps({'success': 'user updated successfully'}), status=200) + except Exception as err: + logger.error(err) + return Response(response=json.dumps({'err_msg': 'Sorry, an error occurred while updating user'}), + status=400) From 2f59c83b120da1fef35729444db09e6a097a0d4c Mon Sep 17 00:00:00 2001 From: Peter Yefi Date: Wed, 11 Jan 2023 19:56:51 -0500 Subject: [PATCH 2/6] Included authorization token --- bootstrap.py | 6 +- .../helpers/__pycache__/auth.cpython-38.pyc | Bin 0 -> 2731 bytes .../__pycache__/session_helper.cpython-38.pyc | Bin 0 -> 4527 bytes hub_api/helpers/auth.py | 73 ++++++++++++++++++ hub_api/user.py | 55 ++++++++++++- requirements.txt | 3 +- 6 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 hub_api/helpers/__pycache__/auth.cpython-38.pyc create mode 100644 hub_api/helpers/__pycache__/session_helper.cpython-38.pyc create mode 100644 hub_api/helpers/auth.py diff --git a/bootstrap.py b/bootstrap.py index 4ea664b..6af6d41 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -30,7 +30,7 @@ from hub_api.energy_demand import EnergyDemand from hub_api.session import SessionStart, SessionEnd, KeepSessionAlive from hub_api.uptime import Uptime from hub_api.greenery import Greenery -from hub_api.user import User +from hub_api.user import User, UserLogin app = flask.Flask('gamification') api = Api(app) @@ -55,6 +55,7 @@ api.add_resource(MaterialLCACatalog, '/v1.4/material_lca_catalog/entries') api.add_resource(MaterialLCACalculations, '/v1.4/material_lca_catalog/calculations') api.add_resource(HeatPump, '/v1.4/heat-pump') api.add_resource(User, '/v1.4/user') +api.add_resource(UserLogin, '/v1.4/user/login') api.add_resource(SessionStart, '/v1.4/session/start') api.add_resource(SessionEnd, '/v1.4/session/end') api.add_resource(KeepSessionAlive, '/v1.4/session/keep_alive') @@ -70,8 +71,9 @@ app.config.update({ openapi_version='2.0.0' ), 'APISPEC_SWAGGER_URL': '/swagger/', # URI to access API Doc JSON - 'APISPEC_SWAGGER_UI_URL': '/swagger-ui/' # URI to access UI of API Doc + 'APISPEC_SWAGGER_UI_URL': '/api-docs/' # URI to access UI of API Doc }) docs = FlaskApiSpec(app) docs.register(HeatPump) docs.register(User) +docs.register(UserLogin) diff --git a/hub_api/helpers/__pycache__/auth.cpython-38.pyc b/hub_api/helpers/__pycache__/auth.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0c963726cab21c5dd6305800f69fd4c5d851d91 GIT binary patch literal 2731 zcma)8OK%)S5bmDW&OZIj!wDoyAV?b$TZ%|d#t@Mp9+DN}L{3IXtMPR2?(EKso?bh< ztRsXYB_M@|xN@-Jl>dOljUUlh4k&j{IZ@TS4mOGrvznT&>Z+SI6}g@Z zI(AfyOI}IKoTwaEyow$dqH0|8Y7^ZlZwhqXXgZ$pX0)sr&Bk-yoF12=`S_T3j1unb z5brp*SotT)Dy;g@@=oxRtOnc^n+9$HxEbJP*&J}En6*Y4^Uq)hq+u$T`I5^p=BkL4 z@ksj0#o;hbc!MhIdKk!tq0EohZ>vgotJiFaG;U@*UWM6XZ7!Q(BFDmgUr(vBw}hYN zpivO~^FGfd44o)#x4D2(@lMXgtu*4OQ3204?gu=RVVXRJ75gw5Pz-SLSnx$;-iJ5; z3J9aSMAELYYcgYx?$K>3tr2DBj`7`R+lI79#;&tRcIdV#3)0;!%2KzyM`eXs%zi>o zz|4p+XPF$F)v^M}s@>X%j7;XT;uCY77~~51l-we}kTw0i3n;@0mR5(QFSeIN?k~bw zJf{zoAvT_!gy3LHb|>wNT$%iSrp(Zno_%ZW@|CMFWBO6MF{PYVDq>$MBh8hS`Ld%* zpz9|L_pYoo*9)$z)C*iOQvje@5{ybemwZ!RGi`BKIu7X#i3m#*`?C(~?+rE%tq z-cs9-!&Vshh|eX^-3$YMY4h?@r@ztkvv8@yqYORxec4&e25PR&6E1LFlr&}9;|a!- zSAY;YODpg@L+7bD4?G^1Ryi8-o1pd|kYD&W=!?+dIgGN2H6Rq6EiOlC;79q&;sNzTVW1gaZyg4nwS*9M=pzd88{@q| zT90>Nt_Gm+;&EVvRpKnX`E}UO@Eb|GZ~zMgE{HKQbUiRpwnHgqIO)0KZDuetq|9m= z>yX!vQEo3_?XaXdvL>1!Va@`PR@d%2Jwqg{pnbawq*nm0xNVKB{7djv0$=5cFQ_sj z`YlDj7Sx(vN&HU9woS?K3|6I)y$5lCO$BVKEyE$}IMfwDxiQakzs>XE*?Vcf z-tjkiJxOJKgY%@0xbj3|wcsrL0Z`DTNZ%e+)aZB8Tq^5BpdSp6qX%D%9iS~P1BO2$ zd{ZAKIw9(rKZsJFh2NvJx-Zp5V|aBf6=HC)?k9CFL@MfO5cCDywfa^kjCeg0X#nv; z#?=QgEDjT8LmYjs+)w!61}-8n zL6of*6g3V&Y&>}(J;hCw4|*A6`+7^6N)J& zTaMGjFldRo`mI6Pbm=*285YIyv{9mUD5v{$c=d>6(_*YlP?S^gPZCZR6Gd1D!dZro zcn8N+hXx-l(g|KW>;%(4$BZM?q0^^^=l|EzdZ)0Gt|cpAyf475j-`6B>LG7+n4GLt zDOS#icY$x5*8Na7z;QoxZ&}R7}6gv*Kcu%|<`sVw8Vsd>abVtxF uPMNs`z%d+*eT+9Y-vh|I%j4n+nF_nb>OGGT1K`*#5qSMI9J5xe75)Z)?4OeW literal 0 HcmV?d00001 diff --git a/hub_api/helpers/__pycache__/session_helper.cpython-38.pyc b/hub_api/helpers/__pycache__/session_helper.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6d82dd266145e38cb12de70d31184d14ecd50ef GIT binary patch literal 4527 zcmb7H&2QVt6(@(JM9H%9<$O5JCfs7ZfQs0bzmlz+4U$dJv_)zd>u%wy9|Wx#%d{m@ z8B*T0LHAJS+Cy(W>>=)?6L{?@w*p0hez(6jlx#XuvI&KmdGq-AzTcbC zXXE2#4bPu{|JRNC?_;G+{<)6L1B|GkYZ})Y8rL~%>kVD4tif7Z!{Dr8Vptdjj3S23 z4Q_Ia7kIH{ahsR8*)n*UkMR=L3ZQXbZJBtZaG*WcD)e^)yaDQrM-^O3{5-$Vr?Ax5 zHowR(f!8U%M0jwjyz+)PK?9|aq5!`OeB#S;qx3}Mm-&_F8ozSL8fAW!Uwf`K#`q+^ zj;Ya@l$MWc4Xf3o~`hp>(F(UW{)Zh21^rx3*&EkH^k@eSY3qmEpE%#?HfDyal?z z>jL7lWM?`<+mRv3Sr7gJvkX@69y5@=GYCt?YmJ3COu0v>vW6#aqjb zFc9lYch;k>*A(ujVYunexplX`|NdFJN1>EH59KMz+*gy#e>KTMUZDlaGllMsP{u{Xw`|o1wyHU^8$|;+xr)<97U<+6mF_r+| z!Fm_tBf#IRr^Z~p-lY7^KL$1rFrr^z5*m7r)?n0kP>F^xslz6{&G6juldJb>@^v* z8axEkc!UvAM@{rZbI~x@v}1kcSWj&?%GyPZrDi1Bn`yC+IM(DjeDKf4>}QdX(QH?s zGwg{?fA+D6cH*!*%fn9BlRL95uY-2d^e8K*LA#4qGrc=MyVcupy{c|HJg6CC&xf^tjlpM z%8+b2Puq)_euj}rWc%my66xc2kyaj<<#Q!dUK*B|GM{hc1Xl9`ef}=utT@~e!D}VS zD-g4p6LldkN_{&djq+I~2z?uCiMFjD>S|?&I8VQ%vxTkHn4G|SC7@=cMifhVd4L#c zba6{~9I3Mns)4I7<<;rGA5!Qwu{}m?FSVo)0TR*0*@hD>_FCfg&Y)XDkduBZFMR~3 zq1ridO%+_Ib|}y8srr$VKq=Ai3}snr?RxE=cs1Eb2Z_=5z>u+&%*y*FfEXz(sZMKp zdj3Q~l$O^HTceyTZ-96gW8mN$BTejcczfX!IKmO(UXzO2eUk$3LHK@70Vl7(C@ycW z#CR`4c;6`fCM5qnCw($6{mkbpwJP-+y1BS+T6Wz|$a`&qE3W&j=e4sp@&dS}c2|Z- zFtics6}ddWMq3JLT8Ko9j;I14$qABl$wi;Kr&Rr>1gL=#eTd1{ZG-9Mp<$lEN&4oD zrzkxv43#U?Okyq8V^rv&{#g6z>H**cE%1QF#+DRm~xeyiN490rX%m@`;P%}YfXDrp}AIANekkOu8*7D#MFN&pr7 z#{jZ6{Q^NgLIND<39cU$e5?pHLauGYz8Gncz{*1d_F4yp#F7tUD=B=eB@|wBf2Qm> zpNdYnD;&@1$IIE2VaJKL!~hXFo|N7mNF}ww+%Zr{O#xFRRYhvFM4XOEaEpcOaW0h^ znR|+9QHp0hf$*PYA<1K^bK>|M=__J*8v9>^Jfft~Op3j&7mx!cW_cI5Gd`m>dQvdI z!-l%|3mj-ieShuP@*4edqQr*}Sym>hT#ymS4)UP|9~z03n97HRgJM#Uzex;kp_taW zbzldT*oq4;ibu$*0~@ftZNDfTX;{njH}jYJReDoO?076GBGb%!T2ku6_B}1GWbmPm zJfmErT_1n&MJWeG~h@dECMFJghE*~9;=ARfd9RLfGtJsB)JDyeQe>X5(f&^=}l zvX#@Ar4|<(z1B$ozyWx;RuNKZZDK@2u zwg#2+ThK&b$D~;mqSx6^br#dHS>26>epF6Q0}5^R3dXGoHhXaUUvB0r^7$` z118f#GBXn<^nZ-${olWtrJ(;ZoEi8HR-OHanSC0<|BDs+yR-O=&)-=oM@zVWF#8ou dMya#EvC3=S3@CK=7xt&Z_o_9!Y8UP5{{dEL3UmMf literal 0 HcmV?d00001 diff --git a/hub_api/helpers/auth.py b/hub_api/helpers/auth.py new file mode 100644 index 0000000..0136627 --- /dev/null +++ b/hub_api/helpers/auth.py @@ -0,0 +1,73 @@ +from datetime import datetime, timedelta, timezone +from typing import Dict +from jwt import JWT, jwk_from_pem +import os +from jwt.utils import get_int_from_datetime +from functools import wraps +from flask import request +import json +from hub_logger import logger +from persistence.models import UserRoles +from jwt.exceptions import JWTException + +instance = JWT() + + +def generate_auth_token(user: Dict): + private_key = "{}/rsa.pem".format(os.path.expanduser('~')) + with open(private_key, 'rb') as fh: + signing_key = jwk_from_pem(fh.read()) + user['exp'] = get_int_from_datetime(datetime.now(timezone.utc) + timedelta(hours=24)) + user['iat'] = get_int_from_datetime(datetime.now(timezone.utc)) + return instance.encode(user, signing_key, alg='RS256') + + +def validate_auth_token(token: str): + public_key = "{}/rsa.pub".format(os.path.expanduser('~')) + with open(public_key, 'rb') as fh: + verifying_key = jwk_from_pem(fh.read()) + return instance.decode(token, verifying_key, do_time_check=True) + + +def role_required(role: str): + def auth_module(user): + return user['role'] == role + + """ + A wrapper to authorize specific roles for specific endpoints + :param roles: a list of roles allowed + :return: + """ + + def role_decorator(f): + @wraps(f) + def wrapper(*args, **kwargs): + try: + token = request.headers['Authorization'] + user = validate_auth_token(token) + + if user is None: + return {'messages': 'You have not been authenticated'}, 401 + allowed = auth_module(user['user']) + + if user['user']['role'] == UserRoles.Admin.value and 'localhost' not in request.headers['Host']: + allowed = False + + if not allowed: + return {'messages': 'You are not authorized'}, 403 + return f(*args, **kwargs) + except KeyError as err: + # logger Error + logger.error(err) + return {'messages': 'Invalid payload'}, 400 + except JWTException as err: + logger.error(err) + return {'messages': 'Invalid token'}, 401 + except Exception as err: + logger.error(err) + return {'messages': 'Sorry, an error occurred while processing your request'}, 500 + + return wrapper + + return role_decorator + diff --git a/hub_api/user.py b/hub_api/user.py index 61ddfb5..caeac10 100644 --- a/hub_api/user.py +++ b/hub_api/user.py @@ -11,17 +11,31 @@ from flask_apispec.views import MethodResource from flask_restful import Resource from marshmallow import Schema, fields from imports.user_factory import UserFactory +from exports.user_factory import UserFactory as ExUserFactory import os from hub_logger import logger +from hub_api.helpers.auth import generate_auth_token, role_required +from persistence.models import UserRoles -class UserPostData(Schema): +class AuthorizationHeader(Schema): + Authorization = fields.Str(required=True, description='Authorization Token') + AppID = fields.Str(required=True, description='ID of Application Accessing API') + + +class LoginPostData(Schema): + """ + Defines post data for users + """ + password = fields.String(required=True, description='Password of user') + email = fields.String(required=True, description='Email of user') + + +class UserPostData(LoginPostData): """ Defines post data for users """ name = fields.String(required=True, description='Name of user') - email = fields.String(required=True, description='Email of user') - password = fields.String(required=True, description='Password of user') role = fields.String(required=True, description='Allowed user roles', enum=['Admin', 'Hub_Reader']) @@ -46,6 +60,8 @@ class User(MethodResource, Resource): dotenv_path="{}/.env".format(os.path.expanduser('~'))) @doc(description='Create users', tags=['CreateUser']) + @role_required(UserRoles.Admin.value) + @use_kwargs(AuthorizationHeader, location='headers') @use_kwargs(UserPostData) def post(self, **kwargs): try: @@ -53,7 +69,7 @@ class User(MethodResource, Resource): user = self.user_factory.create_user(name=kwargs["name"], email=kwargs["email"], password=kwargs["password"], role=kwargs["role"]) if type(user) is dict: - return Response(response=json.dumps(user), status=200) + return Response(response=json.dumps(user), status=400) return Response(response=json.dumps({'user': {'id': user.id, 'name': user.name, 'email': user.email, 'password': user.password, 'role': user.role.value}}), status=201) except Exception as err: @@ -62,6 +78,8 @@ class User(MethodResource, Resource): @doc(description='Get all users', tags=['UpdateUsers']) @use_kwargs(UserPutData) + @role_required(UserRoles.Admin.value) + @use_kwargs(AuthorizationHeader, location='headers') def put(self, **kwargs): try: res = self.user_factory.update_user(user_id=kwargs['id'], name=kwargs['name'], password=kwargs['password'], @@ -73,3 +91,32 @@ class User(MethodResource, Resource): logger.error(err) return Response(response=json.dumps({'err_msg': 'Sorry, an error occurred while updating user'}), status=400) + + +class UserLogin(MethodResource, Resource): + def __init__(self): + self.user_factory = ExUserFactory(db_name='hub_prod', app_env='PROD', + dotenv_path="{}/.env".format(os.path.expanduser('~'))) + + @doc(description='Create users', tags=['LoginUser']) + @use_kwargs(LoginPostData) + def post(self, **kwargs): + try: + user = self.user_factory.login_user(email=kwargs["email"], password=kwargs["password"]) + if type(user) is dict: + return Response(response=json.dumps(user), status=400) + user = user[0] + user_dict = { + 'user': { + 'id': user.id, + 'name': user.name, + 'email': user.email, + 'password': user.password, + 'role': user.role.value, + } + } + user_dict['token'] = generate_auth_token(user_dict) + return Response(response=json.dumps(user_dict), status=201) + except Exception as err: + logger.error(err) + return Response(response=json.dumps({'err_msg': 'Sorry an error occurred while authenticating user'}), status=400) diff --git a/requirements.txt b/requirements.txt index c519ccc..5a2fa80 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,4 +20,5 @@ ply rhino3dm==7.7.0 scipy PyYAML -pyecore==0.12.2 \ No newline at end of file +pyecore==0.12.2 +jwt==1.3.1 \ No newline at end of file From 8c0fed0d9d82e9fafe7b91e6e0bc168bccaadba6 Mon Sep 17 00:00:00 2001 From: Peter Yefi Date: Wed, 11 Jan 2023 19:57:42 -0500 Subject: [PATCH 3/6] Removed compiled files --- hub_api/helpers/__pycache__/auth.cpython-38.pyc | Bin 2731 -> 0 bytes .../__pycache__/session_helper.cpython-38.pyc | Bin 4527 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 hub_api/helpers/__pycache__/auth.cpython-38.pyc delete mode 100644 hub_api/helpers/__pycache__/session_helper.cpython-38.pyc diff --git a/hub_api/helpers/__pycache__/auth.cpython-38.pyc b/hub_api/helpers/__pycache__/auth.cpython-38.pyc deleted file mode 100644 index d0c963726cab21c5dd6305800f69fd4c5d851d91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2731 zcma)8OK%)S5bmDW&OZIj!wDoyAV?b$TZ%|d#t@Mp9+DN}L{3IXtMPR2?(EKso?bh< ztRsXYB_M@|xN@-Jl>dOljUUlh4k&j{IZ@TS4mOGrvznT&>Z+SI6}g@Z zI(AfyOI}IKoTwaEyow$dqH0|8Y7^ZlZwhqXXgZ$pX0)sr&Bk-yoF12=`S_T3j1unb z5brp*SotT)Dy;g@@=oxRtOnc^n+9$HxEbJP*&J}En6*Y4^Uq)hq+u$T`I5^p=BkL4 z@ksj0#o;hbc!MhIdKk!tq0EohZ>vgotJiFaG;U@*UWM6XZ7!Q(BFDmgUr(vBw}hYN zpivO~^FGfd44o)#x4D2(@lMXgtu*4OQ3204?gu=RVVXRJ75gw5Pz-SLSnx$;-iJ5; z3J9aSMAELYYcgYx?$K>3tr2DBj`7`R+lI79#;&tRcIdV#3)0;!%2KzyM`eXs%zi>o zz|4p+XPF$F)v^M}s@>X%j7;XT;uCY77~~51l-we}kTw0i3n;@0mR5(QFSeIN?k~bw zJf{zoAvT_!gy3LHb|>wNT$%iSrp(Zno_%ZW@|CMFWBO6MF{PYVDq>$MBh8hS`Ld%* zpz9|L_pYoo*9)$z)C*iOQvje@5{ybemwZ!RGi`BKIu7X#i3m#*`?C(~?+rE%tq z-cs9-!&Vshh|eX^-3$YMY4h?@r@ztkvv8@yqYORxec4&e25PR&6E1LFlr&}9;|a!- zSAY;YODpg@L+7bD4?G^1Ryi8-o1pd|kYD&W=!?+dIgGN2H6Rq6EiOlC;79q&;sNzTVW1gaZyg4nwS*9M=pzd88{@q| zT90>Nt_Gm+;&EVvRpKnX`E}UO@Eb|GZ~zMgE{HKQbUiRpwnHgqIO)0KZDuetq|9m= z>yX!vQEo3_?XaXdvL>1!Va@`PR@d%2Jwqg{pnbawq*nm0xNVKB{7djv0$=5cFQ_sj z`YlDj7Sx(vN&HU9woS?K3|6I)y$5lCO$BVKEyE$}IMfwDxiQakzs>XE*?Vcf z-tjkiJxOJKgY%@0xbj3|wcsrL0Z`DTNZ%e+)aZB8Tq^5BpdSp6qX%D%9iS~P1BO2$ zd{ZAKIw9(rKZsJFh2NvJx-Zp5V|aBf6=HC)?k9CFL@MfO5cCDywfa^kjCeg0X#nv; z#?=QgEDjT8LmYjs+)w!61}-8n zL6of*6g3V&Y&>}(J;hCw4|*A6`+7^6N)J& zTaMGjFldRo`mI6Pbm=*285YIyv{9mUD5v{$c=d>6(_*YlP?S^gPZCZR6Gd1D!dZro zcn8N+hXx-l(g|KW>;%(4$BZM?q0^^^=l|EzdZ)0Gt|cpAyf475j-`6B>LG7+n4GLt zDOS#icY$x5*8Na7z;QoxZ&}R7}6gv*Kcu%|<`sVw8Vsd>abVtxF uPMNs`z%d+*eT+9Y-vh|I%j4n+nF_nb>OGGT1K`*#5qSMI9J5xe75)Z)?4OeW diff --git a/hub_api/helpers/__pycache__/session_helper.cpython-38.pyc b/hub_api/helpers/__pycache__/session_helper.cpython-38.pyc deleted file mode 100644 index c6d82dd266145e38cb12de70d31184d14ecd50ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4527 zcmb7H&2QVt6(@(JM9H%9<$O5JCfs7ZfQs0bzmlz+4U$dJv_)zd>u%wy9|Wx#%d{m@ z8B*T0LHAJS+Cy(W>>=)?6L{?@w*p0hez(6jlx#XuvI&KmdGq-AzTcbC zXXE2#4bPu{|JRNC?_;G+{<)6L1B|GkYZ})Y8rL~%>kVD4tif7Z!{Dr8Vptdjj3S23 z4Q_Ia7kIH{ahsR8*)n*UkMR=L3ZQXbZJBtZaG*WcD)e^)yaDQrM-^O3{5-$Vr?Ax5 zHowR(f!8U%M0jwjyz+)PK?9|aq5!`OeB#S;qx3}Mm-&_F8ozSL8fAW!Uwf`K#`q+^ zj;Ya@l$MWc4Xf3o~`hp>(F(UW{)Zh21^rx3*&EkH^k@eSY3qmEpE%#?HfDyal?z z>jL7lWM?`<+mRv3Sr7gJvkX@69y5@=GYCt?YmJ3COu0v>vW6#aqjb zFc9lYch;k>*A(ujVYunexplX`|NdFJN1>EH59KMz+*gy#e>KTMUZDlaGllMsP{u{Xw`|o1wyHU^8$|;+xr)<97U<+6mF_r+| z!Fm_tBf#IRr^Z~p-lY7^KL$1rFrr^z5*m7r)?n0kP>F^xslz6{&G6juldJb>@^v* z8axEkc!UvAM@{rZbI~x@v}1kcSWj&?%GyPZrDi1Bn`yC+IM(DjeDKf4>}QdX(QH?s zGwg{?fA+D6cH*!*%fn9BlRL95uY-2d^e8K*LA#4qGrc=MyVcupy{c|HJg6CC&xf^tjlpM z%8+b2Puq)_euj}rWc%my66xc2kyaj<<#Q!dUK*B|GM{hc1Xl9`ef}=utT@~e!D}VS zD-g4p6LldkN_{&djq+I~2z?uCiMFjD>S|?&I8VQ%vxTkHn4G|SC7@=cMifhVd4L#c zba6{~9I3Mns)4I7<<;rGA5!Qwu{}m?FSVo)0TR*0*@hD>_FCfg&Y)XDkduBZFMR~3 zq1ridO%+_Ib|}y8srr$VKq=Ai3}snr?RxE=cs1Eb2Z_=5z>u+&%*y*FfEXz(sZMKp zdj3Q~l$O^HTceyTZ-96gW8mN$BTejcczfX!IKmO(UXzO2eUk$3LHK@70Vl7(C@ycW z#CR`4c;6`fCM5qnCw($6{mkbpwJP-+y1BS+T6Wz|$a`&qE3W&j=e4sp@&dS}c2|Z- zFtics6}ddWMq3JLT8Ko9j;I14$qABl$wi;Kr&Rr>1gL=#eTd1{ZG-9Mp<$lEN&4oD zrzkxv43#U?Okyq8V^rv&{#g6z>H**cE%1QF#+DRm~xeyiN490rX%m@`;P%}YfXDrp}AIANekkOu8*7D#MFN&pr7 z#{jZ6{Q^NgLIND<39cU$e5?pHLauGYz8Gncz{*1d_F4yp#F7tUD=B=eB@|wBf2Qm> zpNdYnD;&@1$IIE2VaJKL!~hXFo|N7mNF}ww+%Zr{O#xFRRYhvFM4XOEaEpcOaW0h^ znR|+9QHp0hf$*PYA<1K^bK>|M=__J*8v9>^Jfft~Op3j&7mx!cW_cI5Gd`m>dQvdI z!-l%|3mj-ieShuP@*4edqQr*}Sym>hT#ymS4)UP|9~z03n97HRgJM#Uzex;kp_taW zbzldT*oq4;ibu$*0~@ftZNDfTX;{njH}jYJReDoO?076GBGb%!T2ku6_B}1GWbmPm zJfmErT_1n&MJWeG~h@dECMFJghE*~9;=ARfd9RLfGtJsB)JDyeQe>X5(f&^=}l zvX#@Ar4|<(z1B$ozyWx;RuNKZZDK@2u zwg#2+ThK&b$D~;mqSx6^br#dHS>26>epF6Q0}5^R3dXGoHhXaUUvB0r^7$` z118f#GBXn<^nZ-${olWtrJ(;ZoEi8HR-OHanSC0<|BDs+yR-O=&)-=oM@zVWF#8ou dMya#EvC3=S3@CK=7xt&Z_o_9!Y8UP5{{dEL3UmMf From 6dc6c6ab19f7143eb1def561a2b9a5053882fe61 Mon Sep 17 00:00:00 2001 From: Peter Yefi Date: Fri, 13 Jan 2023 12:01:13 -0500 Subject: [PATCH 4/6] Included auth token --- bootstrap.py | 4 ++- hub_api/city_info.py | 32 ++++++++++++++++-- .../helpers/__pycache__/auth.cpython-38.pyc | Bin 0 -> 2711 bytes .../__pycache__/session_helper.cpython-38.pyc | Bin 0 -> 4527 bytes hub_api/helpers/auth.py | 4 +-- hub_api/user.py | 25 +++++--------- 6 files changed, 43 insertions(+), 22 deletions(-) create mode 100644 hub_api/helpers/__pycache__/auth.cpython-38.pyc create mode 100644 hub_api/helpers/__pycache__/session_helper.cpython-38.pyc diff --git a/bootstrap.py b/bootstrap.py index 6af6d41..ca6478f 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -10,7 +10,7 @@ from apispec import APISpec from apispec.ext.marshmallow import MarshmallowPlugin from flask_apispec.extension import FlaskApiSpec from flask_restful import Api -from hub_api.city_info import CityInfo +from hub_api.city_info import CityInfo, City from hub_api.geometry import Geometry from hub_api.greenery_catalog import GreeneryCatalogEntries from hub_api.greenery_catalog import GreeneryCatalogEntry @@ -60,6 +60,7 @@ api.add_resource(SessionStart, '/v1.4/session/start') api.add_resource(SessionEnd, '/v1.4/session/end') api.add_resource(KeepSessionAlive, '/v1.4/session/keep_alive') api.add_resource(CityInfo, '/v1.4/city_info') +api.add_resource(City, '/v1.4/city') api.add_resource(Greenery, '/v1.4/greenery') # Add api documentation @@ -77,3 +78,4 @@ docs = FlaskApiSpec(app) docs.register(HeatPump) docs.register(User) docs.register(UserLogin) +docs.register(City) diff --git a/hub_api/city_info.py b/hub_api/city_info.py index 0f5071c..7d290f9 100644 --- a/hub_api/city_info.py +++ b/hub_api/city_info.py @@ -4,11 +4,24 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca """ import json - +from flask_apispec import use_kwargs, doc from flask import Response, request from flask_restful import Resource - from hub_api.helpers.session_helper import refresh_session +from marshmallow import fields, Schema +from hub_api.helpers.auth import role_required +from persistence.models import UserRoles +from flask_apispec.views import MethodResource +from hub_logger import logger + + +class AuthorizationHeader(Schema): + Authorization = fields.Str(required=True, description='Authorization token') + AppID = fields.Str(required=True, description='ID of app accessing API') + + +class CitySchema(Schema): + city_file = fields.Raw(type='file', required=True, description='City file') class CityInfo(Resource): @@ -70,3 +83,18 @@ class CityInfo(Resource): 'buildings': buildings } return Response(json.dumps(response), headers=headers) + + +class City(MethodResource, Resource): + @doc(description='Persist a city', tags=['PersistCity']) + @role_required(UserRoles.Admin.value) + @use_kwargs(AuthorizationHeader, location='headers') + @use_kwargs(CitySchema) + def post(self, **kwargs): + try: + print(kwargs) + print(request.files) + return Response(response=json.dumps({'msg': 'Hello'}), status=201) + except Exception as err: + logger.error(err) + return Response(response=json.dumps({'err_msg': 'Sorry an error occurred while creating user'}), status=400) diff --git a/hub_api/helpers/__pycache__/auth.cpython-38.pyc b/hub_api/helpers/__pycache__/auth.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8fc58f46cbff98eaadfa3090a6499e78dec5b868 GIT binary patch literal 2711 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 literal 0 HcmV?d00001 diff --git a/hub_api/helpers/auth.py b/hub_api/helpers/auth.py index 0136627..fa23250 100644 --- a/hub_api/helpers/auth.py +++ b/hub_api/helpers/auth.py @@ -4,8 +4,7 @@ from jwt import JWT, jwk_from_pem import os from jwt.utils import get_int_from_datetime from functools import wraps -from flask import request -import json +from flask import request, g from hub_logger import logger from persistence.models import UserRoles from jwt.exceptions import JWTException @@ -31,6 +30,7 @@ def validate_auth_token(token: str): def role_required(role: str): def auth_module(user): + g.user = user return user['role'] == role """ diff --git a/hub_api/user.py b/hub_api/user.py index caeac10..1a3beb8 100644 --- a/hub_api/user.py +++ b/hub_api/user.py @@ -4,7 +4,6 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Project Author Peter Yefi peteryefi@gmail.com """ import json - from flask import Response from flask_apispec import use_kwargs, doc from flask_apispec.views import MethodResource @@ -19,11 +18,11 @@ from persistence.models import UserRoles class AuthorizationHeader(Schema): - Authorization = fields.Str(required=True, description='Authorization Token') - AppID = fields.Str(required=True, description='ID of Application Accessing API') + Authorization = fields.Str(required=True, description='Authorization token') + AppID = fields.Str(required=True, description='ID of app accessing API') -class LoginPostData(Schema): +class LoginPostSchema(Schema): """ Defines post data for users """ @@ -31,7 +30,7 @@ class LoginPostData(Schema): email = fields.String(required=True, description='Email of user') -class UserPostData(LoginPostData): +class UserPostSchema(LoginPostSchema): """ Defines post data for users """ @@ -39,21 +38,13 @@ class UserPostData(LoginPostData): role = fields.String(required=True, description='Allowed user roles', enum=['Admin', 'Hub_Reader']) -class UserPutData(UserPostData): +class UserPutSchema(UserPostSchema): """ Defines put data for users """ id = fields.Int(required=True, description='The Id of the user to be Updated') -class UserLoginData(Schema): - """ - Defines post data for users - """ - email = fields.String(required=True, description='Email of user') - password = fields.String(required=True, description='Password of user') - - class User(MethodResource, Resource): def __init__(self): self.user_factory = UserFactory(db_name='hub_prod', app_env='PROD', @@ -62,7 +53,7 @@ class User(MethodResource, Resource): @doc(description='Create users', tags=['CreateUser']) @role_required(UserRoles.Admin.value) @use_kwargs(AuthorizationHeader, location='headers') - @use_kwargs(UserPostData) + @use_kwargs(UserPostSchema) def post(self, **kwargs): try: @@ -77,7 +68,7 @@ class User(MethodResource, Resource): return Response(response=json.dumps({'err_msg': 'Sorry an error occurred while creating user'}), status=400) @doc(description='Get all users', tags=['UpdateUsers']) - @use_kwargs(UserPutData) + @use_kwargs(UserPutSchema) @role_required(UserRoles.Admin.value) @use_kwargs(AuthorizationHeader, location='headers') def put(self, **kwargs): @@ -99,7 +90,7 @@ class UserLogin(MethodResource, Resource): dotenv_path="{}/.env".format(os.path.expanduser('~'))) @doc(description='Create users', tags=['LoginUser']) - @use_kwargs(LoginPostData) + @use_kwargs(LoginPostSchema) def post(self, **kwargs): try: user = self.user_factory.login_user(email=kwargs["email"], password=kwargs["password"]) From 82b3e626c7f2ede74bc72d526b2ec12cf9496622 Mon Sep 17 00:00:00 2001 From: Peter Yefi Date: Fri, 13 Jan 2023 12:01:51 -0500 Subject: [PATCH 5/6] Removed compiled files --- hub_api/helpers/__pycache__/auth.cpython-38.pyc | Bin 2711 -> 0 bytes .../__pycache__/session_helper.cpython-38.pyc | Bin 4527 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 hub_api/helpers/__pycache__/auth.cpython-38.pyc delete mode 100644 hub_api/helpers/__pycache__/session_helper.cpython-38.pyc diff --git a/hub_api/helpers/__pycache__/auth.cpython-38.pyc b/hub_api/helpers/__pycache__/auth.cpython-38.pyc deleted file mode 100644 index 8fc58f46cbff98eaadfa3090a6499e78dec5b868..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2711 zcma(TO^+MJvb*QQ5VltRn=Y9)%EH3I-QWf^XuME z$$tYven#Tt;{fq8yy`~)oN$_voJ1t25zSe|DDqh5e-RdgS5~!^Nz0Xsqyw@Tq%%i zNb%83C~fEn_WM~m8VLzl^F1Zx)iM(bh&CvH?M5Ohot6biyE9l!DB5`ZxbVfm{1aaF zDF99niKY{F=x}yKkLUr_?virnCi~Ba2TXfQcIY3GoAkiZ4ILadbvS7qQQhV)_r9R_ z!^)Cy|1vqbYIzOdbtc^JLD?)j$E_f_aVsO#BgU3etfVkRlEi6 z_?>=YnB4v40Wc1(aQ4oSjs%shLy^=RjkJ* zguQXW@$SYgD^bV>=rXPpF_(~wL^7nEqTHA0597>qA~7LNkQPeEMIwwZilpSC@5(h; zh+36Q_aMZBshAr!97ipcj*7GxA>(@@ABRo9zrBsYP}`NzLe9l7-F{W5sV=K+UglLS zr`w}APlss|V|*^c-n}#t7x$ju9?y0Mag}b5MOLAPc&5i&)!b~1L?I-ei=2Tjr=nPa zqf{FJq3g5_zw`7Al}`eV?y_6%9rCwf+y4mpg};Y<64(G@u2K?MIm@DKtFl01*#5dg;L;y3_LAsJS)-7HPe-SRPD zM4de$)8YI$1U3LQcmlU??8RA%b#%gUm{AJjf5&n10^nFC66)ua&oDtSA@Egb;%z#l zoN{(6_?+A(fIHm16&%ndg#`9?P6=bZp#pS)LT4DV$UGJY?AgKEgSc|&{dYi&^Sba69^7=x~P68YLe0;#LX*>+CdJe9)xS`1e{IN+OU)Yjidw?=0 z(?DL}j5{gi?vP!F;{FWF-`m6n;?@}VH;Hy8?!=oi`5|vuO~EFaHh^lvE>{(xBLp3- zV;$hSCH*%=eJ;`dG?YJ3a^O+2IIkOr!O}aT(90QbgG^`0d~%(9K)wYxbJ+EX_dZa? z4UlYtfWvpMGs>r3XP3I@boM2c%31bGNx%GJuYDuxNiV_eQ_UZh-Iw+ zW$_pg{2D{G@~^_yLa&PFSsC;6OXPO$aBaU>T)I|DIe)en7d;_mDSKs-%p~MhZ-1OZ z=2fyxz+Nb}-W>XI?|2xczk#lPOSaIi#u)l5G%s6KvWqP56`rSs@xXwYFu_}5{u*kR z9lK~xlGx5n^UtZ(_l+CNkuv^tAHNc--`?Id;w&ro1&3iEB~;Eul%&HG)zZLe2@MpB zNIeaJFh)Zfuz+@Hn>|Wh<|0N{Sx9?y71nytL_@l`bdPGXZ0tnPgiHBXl^q`>n{Wo0 zav47IMZ~E!3qF=*Yq)z_2{s*tC52_YdT#N=|8=yzC7gsQAg>_6CUNYAnrjd1oYH11 zwmz#Jw$IC#Q0759|Lnl4=bx|V6@*rLuo^j&eJxR>Zphdvq9*XRW;)H3ou8&ToE3>K zOQ5kY7;l)xYHHKK_A?XWK&$_w)WKfIv6du%wy9|Wx#%d{m@ z8B*T0LHAJS+Cy(W>>=)?6L{?@w*p0hez(6jlx#XuvI&KmdGq-AzTcbC zXXE2#4bPu{|JRNC?_;G+{<)6L1B|GkYZ})Y8rL~%>kVD4tif7Z!{Dr8Vptdjj3S23 z4Q_Ia7kIH{ahsR8*)n*UkMR=L3ZQXbZJBtZaG*WcD)e^)yaDQrM-^O3{5-$Vr?Ax5 zHowR(f!8U%M0jwjyz+)PK?9|aq5!`OeB#S;qx3}Mm-&_F8ozSL8fAW!Uwf`K#`q+^ zj;Ya@l$MWc4Xf3o~`hp>(F(UW{)Zh21^rx3*&EkH^k@eSY3qmEpE%#?HfDyal?z z>jL7lWM?`<+mRv3Sr7gJvkX@69y5@=GYCt?YmJ3COu0v>vW6#aqjb zFc9lYch;k>*A(ujVYunexplX`|NdFJN1>EH59KMz+*gy#e>KTMUZDlaGllMsP{u{Xw`|o1wyHU^8$|;+xr)<97U<+6mF_r+| z!Fm_tBf#IRr^Z~p-lY7^KL$1rFrr^z5*m7r)?n0kP>F^xslz6{&G6juldJb>@^v* z8axEkc!UvAM@{rZbI~x@v}1kcSWj&?%GyPZrDi1Bn`yC+IM(DjeDKf4>}QdX(QH?s zGwg{?fA+D6cH*!*%fn9BlRL95uY-2d^e8K*LA#4qGrc=MyVcupy{c|HJg6CC&xf^tjlpM z%8+b2Puq)_euj}rWc%my66xc2kyaj<<#Q!dUK*B|GM{hc1Xl9`ef}=utT@~e!D}VS zD-g4p6LldkN_{&djq+I~2z?uCiMFjD>S|?&I8VQ%vxTkHn4G|SC7@=cMifhVd4L#c zba6{~9I3Mns)4I7<<;rGA5!Qwu{}m?FSVo)0TR*0*@hD>_FCfg&Y)XDkduBZFMR~3 zq1ridO%+_Ib|}y8srr$VKq=Ai3}snr?RxE=cs1Eb2Z_=5z>u+&%*y*FfEXz(sZMKp zdj3Q~l$O^HTceyTZ-96gW8mN$BTejcczfX!IKmO(UXzO2eUk$3LHK@70Vl7(C@ycW z#CR`4c;6`fCM5qnCw($6{mkbpwJP-+y1BS+T6Wz|$a`&qE3W&j=e4sp@&dS}c2|Z- zFtics6}ddWMq3JLT8Ko9j;I14$qABl$wi;Kr&Rr>1gL=#eTd1{ZG-9Mp<$lEN&4oD zrzkxv43#U?Okyq8V^rv&{#g6z>H**cE%1QF#+DRm~xeyiN490rX%m@`;P%}YfXDrp}AIANekkOu8*7D#MFN&pr7 z#{jZ6{Q^NgLIND<39cU$e5?pHLauGYz8Gncz{*1d_F4yp#F7tUD=B=eB@|wBf2Qm> zpNdYnD;&@1$IIE2VaJKL!~hXFo|N7mNF}ww+%Zr{O#xFRRYhvFM4XOEaEpcOaW0h^ znR|+9QHp0hf$*PYA<1K^bK>|M=__J*8v9>^Jfft~Op3j&7mx!cW_cI5Gd`m>dQvdI z!-l%|3mj-ieShuP@*4edqQr*}Sym>hT#ymS4)UP|9~z03n97HRgJM#Uzex;kp_taW zbzldT*oq4;ibu$*0~@ftZNDfTX;{njH}jYJReDoO?076GBGb%!T2ku6_B}1GWbmPm zJfmErT_1n&MJWeG~h@dECMFJghE*~9;=ARfd9RLfGtJsB)JDyeQe>X5(f&^=}l zvX#@Ar4|<(z1B$ozyWx;RuNKZZDK@2u zwg#2+ThK&b$D~;mqSx6^br#dHS>26>epF6Q0}5^R3dXGoHhXaUUvB0r^7$` z118f#GBXn<^nZ-${olWtrJ(;ZoEi8HR-OHanSC0<|BDs+yR-O=&)-=oM@zVWF#8ou dMya#EvC3=S3@CK=7xt&Z_o_9!Y8UP5{{dEL3UmMf From 8628dec2474ef4d5830caf0b64b2cedfcdf232b6 Mon Sep 17 00:00:00 2001 From: Peter Yefi Date: Fri, 13 Jan 2023 18:41:14 -0500 Subject: [PATCH 6/6] Fixed minor issues --- hub_api/city_info.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/hub_api/city_info.py b/hub_api/city_info.py index 7d290f9..30f29cd 100644 --- a/hub_api/city_info.py +++ b/hub_api/city_info.py @@ -92,8 +92,6 @@ class City(MethodResource, Resource): @use_kwargs(CitySchema) def post(self, **kwargs): try: - print(kwargs) - print(request.files) return Response(response=json.dumps({'msg': 'Hello'}), status=201) except Exception as err: logger.error(err)