From ba4314435e3584fae2c3df8b650cf40aec1d2038 Mon Sep 17 00:00:00 2001 From: lucasbpro Date: Thu, 7 Jan 2021 21:57:15 -0300 Subject: [PATCH] feat: added security functionality, recipe-materials relationship --- app.py | 17 +++++++++++------ data.db | Bin 28672 -> 32768 bytes models/recipe.py | 6 +++--- models/user.py | 30 ++++++++++++++++++++++++++++++ resources/recipe.py | 30 +++++++++++++++++++++++------- security.py | 11 +++++++++++ 6 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 models/user.py create mode 100644 security.py diff --git a/app.py b/app.py index 73e0e76..5c5c7dc 100644 --- a/app.py +++ b/app.py @@ -3,8 +3,8 @@ from flask import Flask from flask_restful import Api from flask_cors import CORS -# from flask_jwt import JWT -#from security import authenticate, identity +from flask_jwt import JWT +from security import authenticate, identity # import resorces from resources.raw_material import * @@ -19,15 +19,14 @@ app.config['DEBUG'] = True app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///data.db') app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False - app.secret_key = 'rebequinha' # creates API instance CORS(app) api = Api(app) - -# jwt = JWT(app, authenticate, identity) # /auth +# creates jwt functionality for user authentication (/auth) +jwt = JWT(app, authenticate, identity) # Sets up API endpoints api.add_resource(RawMaterial, '/raw_materials/') @@ -44,7 +43,6 @@ api.add_resource(Orders, '/orders') # api.add_resource(UserRegister, '/register') - if __name__ == '__main__': from db import db @@ -55,4 +53,11 @@ def create_tables(): db.create_all() + # creates an admin user before initializing the app + from models.user import UserModel + adminUser = UserModel("admin",os.environ.get('ADMIN_PASSWORD', 'testeAdmin')) + adminUser.save_to_db() + app.run(port=5000) + + diff --git a/data.db b/data.db index 1da1fbd36df6e872daaba63e0860b500e77e8583..91d8993d28a4abda6c5155e43e5983ee45c5be3a 100644 GIT binary patch literal 32768 zcmeI)&uipV00;2AOn%ICJ9)cy*RgijW)HG0?2q}eWw(W;j%BtMXIq_C*##k_Gg%Ft znam~`Ydr|tqW^(-5cD7Lu!1*P#X~`1Pa=Y%B8XmO5!{P-5MMIClId$Nin!$)m`UC@ zd5^qLUP51JyZU_D_AULU<2FoRF9=5kLWHMuT@ZvU`y|J`|UM7H9;-Orn&CgRnxbfX8(vn?1+}R z)v%iW@ZL_-HIw}`+iSU7uj%-fHyl1!S-MuN+|Zvb-O#7(T6hXqS1P5Y#pNKI8gy$~ zuavHqDy8Lx(yH!SRlCJn<5h|DIaa5acWRAHZ=aFXOrbz_+MzxB!P|0ev+fz|TSljS zPNc-H3g>{}5_M`v%wt!oS82cfF_L~$XZ1+EJxu+>VmF?_Zbxh8#og z#|Mo5rrFpt-mHdc4~BPl5U$&<|B4YCp51{TV`n3AWcv2?gqoR{AUkS!o?XrEN{XFh zUwYsBvu1f!*KYA{sJglCxJK3SeEn*9rMMQ}hV53XzBLq9T`RmJ5z}quq*i zt-EV)*R~~5&73(yb{0Ebi(Yg$T1dpZ8QpcjiCm3-nj|HT{f!K;NO;v_Y@a1$v&2Q%USbzGCZ719e&)Eeb6F)2($;~+dulES5E zsMjn#e2=6zomZXUkYN6=Xs-+OQ~CzIqWz_P%nDc_009U<00Izz00bZa0SG_<0{@!8 zyrK%Tgq%CK%Ki$WX|ai{<+Uu|thVcp=QuLWUb>LYUC3wiIX$17%U&EV7iCqr!f(KI zb<4HlO=RaTUWzsml&?stFtcA1d#V?Psu$)H`*wkyL2kH#!c4q@!pyvQPy=~sJYFG} zkCtM43xVDtUE>>a@G z?Ee7%M8Bh7u{Qws9xe!+Ed(F{0SG_<0uX=z1Rwwb2teTQ31osUsS*k1{V7EvDIzAr zqLNTMYf%YfMGlK>@*fuEL|BwWQD&7=5`O>x@Qn@h2m%m*00bZa0SG_<0uX=z1RyXh F@Hf}pHbVda delta 702 zcmZuv&x_MQ6rM?w#!Z_{7FU+i(j6(?UD4P{+KMf#?aJc8ALz3F1JiWu2HK<~QSoG} z2p&9Ga}Yd5@^S$pqW-t&2U*YpLRR;f- zG&)y1sk$Jk41xAj%d7WNztjz781L#!O778vq`aERz$Z6C+UhdTZ!dqi7Tp68KNj>7KW}o>?ssvt-sTRtr!9g)+E)*QNo!?9&eIPTW@C_a^w+RW#-jw)sXba=C;RB=W1PqwVa&Gl?YtAzA~*DN!U>Xv=qO+Vq`%j9XG8RSng2K5`POJy F`wcsFulxW2 diff --git a/models/recipe.py b/models/recipe.py index c0de909..7609fb4 100644 --- a/models/recipe.py +++ b/models/recipe.py @@ -31,7 +31,7 @@ def json(self): 'last_update' : self.last_update, 'labor_cost' : self.labor_cost, 'supply_cost' : self.supply_cost, - 'materials' : self.materials + 'materials' : [material.id for material in self.materials] } def save_to_db(self): @@ -50,5 +50,5 @@ def find_by_description(cls, description): def find_by_id(cls, id_): return cls.query.filter_by(id=id_).first() - def get_all_materials(self): - return self.materials + def get_materials(self): + return [material.json() for material in self.materials] diff --git a/models/user.py b/models/user.py new file mode 100644 index 0000000..7eafb49 --- /dev/null +++ b/models/user.py @@ -0,0 +1,30 @@ +from db import db + +class UserModel(db.Model): + __tablename__ = 'users' + + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80)) + password = db.Column(db.String(80)) + + def __init__(self, username, password): + self.username = username + self.password = password + + def save_to_db(self): + db.session.add(self) + db.session.commit() + + def json(self): + return {'id' : self.id, + 'username' : self.username, + 'password' : self.password, + } + + @classmethod + def find_by_username(cls, username): + return cls.query.filter_by(username=username).first() + + @classmethod + def find_by_id(cls, _id): + return cls.query.filter_by(id=_id).first() \ No newline at end of file diff --git a/resources/recipe.py b/resources/recipe.py index 3c82b3a..cb8eb15 100644 --- a/resources/recipe.py +++ b/resources/recipe.py @@ -8,6 +8,7 @@ # import model from models.recipe import RecipeModel +from models.raw_material import RawMaterialModel class Recipe(Resource): @@ -16,6 +17,7 @@ class Recipe(Resource): parser.add_argument('description',type=str,required=False) parser.add_argument('labor_cost',type=float,required=False) parser.add_argument('supply_cost',type=float,required=False) + parser.add_argument('materials',type=int, action='append',required=False) # to handle HTTP GET /recipe?id= def get(self, id): @@ -53,12 +55,21 @@ def put(self, id): if key=='sell_by_date': recipe.sell_by_date = data['sell_by_date'] if key=='materials': - pass + recipe.materials.clear() + for id in data['materials']: + material = RawMaterialModel.find_by_id(id) + if material: + recipe.materials.append(material) + recipe.last_update = datetime.now().strftime("%d/%m/%Y %H:%M") # in case not exist, creates a new item else: - recipe = RecipeModel(**data) + recipe = RecipeModel(data['description'],data['labor_cost'],data['supply_cost']) + for id in data['materials']: + material = RawMaterialModel.find_by_id(id) + if material: + recipe.materials.append(material) # tries to insert in database # returns 500 (internal server error) in case of database failure @@ -77,6 +88,7 @@ class Recipes(Resource): parser.add_argument('description',type=str,required=True) parser.add_argument('labor_cost',type=float,required=True) parser.add_argument('supply_cost',type=float,required=True) + parser.add_argument('materials',type=int, action='append',required=True) # handles HTTP request GET /recipes def get(self): @@ -96,8 +108,14 @@ def post(self): # in case it does not exist, creates a new recipe using data passed # along with the HTTP request - recipe = RecipeModel(**data) - + recipe = RecipeModel(data['description'],data['labor_cost'],data['supply_cost']) + + # links the recipe to all related materials + for id in data['materials']: + material = RawMaterialModel.find_by_id(id) + if material: + recipe.materials.append(material) + # tries to insert in database # returns 500 (internal server error) in case of database failure try: @@ -112,10 +130,8 @@ def post(self): class MaterialList(Resource): # route: recipe//materials def get(self, id): - recipe = RecipeModel.find_by_id(id) - if recipe: - return {[x.json() for x in recipe.get_all_materials()]} + return recipe.get_materials() else: return {'message' : constants['ID_NOT_FOUND']} \ No newline at end of file diff --git a/security.py b/security.py new file mode 100644 index 0000000..1107206 --- /dev/null +++ b/security.py @@ -0,0 +1,11 @@ +from werkzeug.security import safe_str_cmp +from models.user import UserModel + +def authenticate(username, password): + user = UserModel.find_by_username(username) + if user and safe_str_cmp(password,user.password): + return user + +def identity(payload): + user_id = payload['identity'] + return UserModel.find_by_id(user_id) \ No newline at end of file