def set_new_password(user): """updates a user password from the reset page""" try: data = request.get_json() email = user["email"] new_password = data["password"] check_for_whitespace(data, ["email", "password"]) isValidEmail(email) isValidPassword(new_password) User.query.filter_by(email=user["email"]).update( dict(password=f"{generate_password_hash(str(new_password))}")) db.session.commit() subject = """Password updated successfully.""" content = f""" {password_reset_success_content()} <a href="/forgot" style="{button_style()}" >Forgot Password</a> {email_signature()} """ send_mail(email, subject, content) return custom_make_response( "data", "Your password has been updated successfully", 200) except Exception as e: abort( custom_make_response("error", f"The following error ocurred: {e}", 400))
def close_or_suspend_project(user, id): """ close or suspend the project. """ if user['role'] != "Creator": abort( custom_make_response( "error", "You are not authorized to carryout this function", 403 ) ) try: data = request.get_json() project_status = data['new_status'] check_for_whitespace(data, ["new_status"]) update_project_params = { "project_status": project_status } Project.query.filter_by(id=id).update(update_project_params) db.session.commit() return custom_make_response( "data", f"Project {project_status} successfully.", 200) except Exception as e: abort( custom_make_response( "error", f"The following error occured :: {e}", 400 ) )
def authorize_or_reject_withdraw(user, id): """authorize or reject file""" try: data = request.get_json() new_status = data["new_status"] advance_amount = data["advance_amount"] fileId = id date_actioned = datetime.datetime.utcnow() todays_date = date_actioned.strftime("%Y-%m-%d") file_status = new_status check_for_whitespace(data, ["new_status", "file_id"]) new_file_params = { "fileStatus": file_status, "dateAuthorizedOrRejected": todays_date, "authorizedOrRejectedBy": user["id"], } Files.query.filter_by(id=fileId).update(new_file_params) if data["new_status"] == "Authorized": returned_file, project_id, file_type = get_authorized_files(id) returned_file["advance_amount"] = float(advance_amount) authorized_file = Payments(**returned_file) db.session.add(authorized_file) db.session.commit() if file_type == "Payment": _budget_file = Files.query.\ filter_by(projectId=project_id).\ filter_by(fileType="Budget").all() budget_file = files_schema.dump(_budget_file) budget_amount = budget_file[0]["fileAmount"] new_budget_amount = budget_amount - float(advance_amount) new_amount = {"fileAmount": new_budget_amount} Files.query.filter_by(projectId=project_id).\ filter_by(fileType="Budget").update(new_amount) payment_file = get_individual_payment_amount(id) for file in payment_file: auth_advance_amount = { "advance_amount": file['total_amount'] } Payments.query.filter(Payments.file_id == id).\ filter(Payments.id == file['id']).\ update(auth_advance_amount) db.session.commit() return custom_make_response("data", f"File {file_status} successfully.", 200) except Exception as e: abort( custom_make_response("error", f"The following error occured :: {e}", 400))
def reactivate_system_user(user): """ reactivate a suspended user account check if account is suspended if not then notify the user no need proceeding if account if already active. """ try: data = request.get_json() email = data["email"] check_for_whitespace(data, ["email"]) isValidEmail(email) employee = employee_schema.dump( Employees.query.filter_by(companyId=user['companyId']).filter_by( email=email).first()) if not employee: abort(403) user = user_schema.dump( User.query.filter_by(email=email).filter_by( companyId=user['companyId']).first()) if employee and not user: abort(404) if user['isActive'] == 'true': abort(400) User.query.filter_by(email=email).update(dict(isActive="true")) db.session.commit() return custom_make_response("data", "User account activated successfully", 200) except Exception as e: if (e.code == 400): return custom_make_response("error", "The user account is already active.", 400) elif (e.code == 403): return custom_make_response( "error", "The user whose account you are trying to activate \ is not a member of your company", 403) elif (e.code == 404): return custom_make_response( "error", "You are trying to activate an account\ that does not exist, Please create one.", 404)
def user_login(): try: data = request.get_json() email = data['email'] password = data['password'] except KeyError: abort(utils.response_fn(400, "error", "Should be email & password")) # check for the validity of the email v2utils.isEmailValid(email) # check if both values are stirngs utils.check_for_strings(data, ["email", "password"]) # check for whitespaces. utils.check_for_whitespace(data, ["email", "password"]) # try to get the record of the user by email. try: user = UserModel.get_user_by_mail(email) if not user: abort(utils.response_fn(404, "error", "User does not exist")) id = user[0][0] username = user[0][1] hashed_password = user[0][2] is_admin_prop = user[0][4] password = UserModel.check_if_password_n_hash_match( hashed_password, password) if not password: abort( utils.response_fn(400, "error", "The password is wrong, try again")) token = jwt.encode({ "email": email, "isAdmin": is_admin_prop }, KEY, algorithm='HS256') return utils.response_fn( 200, "data", { "message": "Logged in successfully", "token": token.decode('UTF-8'), "user": { "id": id, "username": username } }) except psycopg2.DatabaseError as _error: abort(utils.response_fn(500, "error", "Server error"))
def suspend_system_user(user): """ suspend a system user """ try: data = request.get_json() email = data["email"] check_for_whitespace(data, ["email"]) isValidEmail(email) employee = employee_schema.dump( Employees.query.filter_by(companyId=user['companyId']).filter_by( email=email).first()) if not employee: abort(400) user = user_schema.dump( User.query.filter_by(email=email).filter_by( companyId=user['companyId']).first()) if employee and not user: abort(404) if user['isActive'] == 'false': abort(403) User.query.filter_by(email=email).update(dict(isActive="false")) db.session.commit() return custom_make_response("data", "User account suspended successfully", 200) except Exception as e: if (e.code == 400): return custom_make_response( "error", "The user you are trying to suspend \ is not a member of your company", 400) elif (e.code == 404): return custom_make_response( "error", "The employee you are trying to\ suspend is not a system user.", 404) elif (e.code == 403): return custom_make_response( "error", "The user account is already suspended.", 403) else: return custom_make_response( "error", "An internal server error occured,\ the site admin has been notified, \ please give it a moment and try again.", 500)
def create_office(user): """ This method allows the admin to creates a specific office to the database """ try: userAdminProperty = user[0][2] except: return utils.response_fn( 401, "error", "You don't have an account, Create one first") try: data = request.get_json() name = data['name'] type = data["type"] except KeyError: abort( utils.response_fn(400, "error", "Should be name & type, enter both fields")) try: """ if email matches, admin's then create the party """ # check if details are for an admin. isUserAdmin(userAdminProperty) # check if inputs are all strings utils.check_for_strings(data, ["name", "type"]) # check if fields are blank utils.check_for_whitespace(data, ["name", "type"]) check_matching_items_in_db_table({"name": name}, "offices") newoffice = OfficesModel(name=name, type=type) id = newoffice.save_office() return utils.response_fn(201, "data", [{ "name": name, "id": id, "type": type }]) except psycopg2.DatabaseError as _error: abort(utils.response_fn(500, "error", "Server error"))
def update_project_number(user): """ update project/job number for a given company """ try: data = request.json() companyId = user['companyId'] projectNumber = data['projectNumber'] check_for_whitespace(data, ["projectNumber"]) update_project_number = {"projectNumber": projectNumber + 1} ProjectNumber.query.\ filter_by(companyId=companyId).update(update_project_number) db.session.commit() return custom_make_response("data", "Project number updated successfully.", 200) except Exception as e: abort( custom_make_response("error", f"The following error occured :: {e}", 400))
def create_party(user): """ This method allows the admin to creates a specific party to the database """ try: userAdminProp = user[0][2] except: return utils.response_fn(401, "error", "You don't have an account. Create One") try: data = request.get_json() name = data['name'] hqAddress = data["hqAddress"] logoUrl = data.get("logoUrl", "") except KeyError: abort(utils.response_fn(400, "error", "Should be name, hqAddress & logoUrl")) # check for the datatype utils.check_for_strings(data, ["name", "hqAddress", "logoUrl"]) # check for whitespaces. utils.check_for_whitespace(data, ["name", "hqAddress"]) try: """ if email matches, admin's then create the party """ # check if the user is an admin isUserAdmin(userAdminProp) newparty = PartiesModel( name=name, hqAddress=hqAddress, logoUrl=logoUrl) check_matching_items_in_db_table({"name": name}, "parties") id = newparty.save_party() return utils.response_fn(201, "data", [{ "name": name, "id": id }]) except psycopg2.DatabaseError as _error: abort(utils.response_fn(500, "error", "Server error"))
def specify_job_number(user): """ specify a job/project number for the projects of a given company """ try: data = request.get_json() project_number = data['projectNumber'] # current_user = User.query.filter_by(id=user["id"]).first() # _data = user_schema.dump(current_user) companyId = user["companyId"] id = generate_db_ids() check_for_whitespace(data, ["projectNumber", "companyId"]) if ProjectNumber.query.filter_by(companyId=companyId).first(): abort(409) new_project_number = ProjectNumber(id=id, projectNumber=project_number, companyId=companyId) db.session.add(new_project_number) db.session.commit() return custom_make_response( "data", f"Project number {project_number} set successfully.", 201) except Exception as e: if (e.code == 409): return custom_make_response( "error", """ You already have a project/job number set for your company. It can only be set once. """, 409, ) else: return custom_make_response( "error", f"{e} One or more mandatory fields has not been filled!", 400)
def update_party(user, party_id): """ This method updates a party if it exists """ try: """ is the name attr in the payload of the request? if not throw an error """ data = request.get_json() name = data['name'] except KeyError: abort(utils.response_fn(400, "error", "Provide a name to update")) try: """ is the isAdmin prop is present or empty if its empty then the user does not have an account. """ userAdminProp = user[0][2] except: return utils.response_fn(401, "error", "You don't have an account") # check if the user is an admin isUserAdmin(userAdminProp) # check if data we want to apply strip on is actually a string. utils.check_for_strings(data, ["name"]) # check if data is present ans is not just an empty string.. utils.check_for_whitespace(data, ["name"]) party = PartiesModel.get_specific_party(party_id) if party: # update party here PartiesModel.update_specific_party(name=name, party_id=party_id) return utils.response_fn(200, "data", [{ "name": name, "id": party_id }]) return utils.response_fn(404, "error", "Party is not found")
def create_project_region(user, projectId): """ create project regions """ try: data = request.get_json() region = data['region'] projectId = data['projectId'] id = generate_db_ids() check_for_whitespace(data, ["region", "projectId", "id"]) if ProjectRegions.query.filter_by(region=data['region']).\ filter_by(projectId=data['projectId']).first(): abort(409) new_region = ProjectRegions(id=id, projectId=projectId, region=region) db.session.add(new_region) db.session.commit() return custom_make_response("data", f" {region} region created successfully.", 201) except Exception as e: if (e.code == 409): return custom_make_response( "error", """ You already have a region with that name for the selected project, Please change and try again ! """, 409, ) else: return custom_make_response( "error", f"{e} One or more mandatory fields has not been filled!", 400)
def insert_admin_employee(company): """ insert admin details to the employee table. """ try: user_data = request.get_json() id = generate_db_ids() this_company = Company.query.filter_by( company=user_data["company"]).first() _company = company_schema.dump(this_company) companyId = _company["id"] fullname = user_data["fullname"] # mobile = user_data["mobile"] email = user_data["email"] password = user_data["password"] role = user_data["role"] isActive = user_data["isActive"] # check data for sanity incase it bypass js on the frontend check_for_whitespace( user_data, [ "companyId", "fullname", "email", # "mobile", "password", "role", "isActive", ], ) if (company['id'] != companyId): abort( custom_make_response( "error", "There is a mismatch in your token info,\ we could not complete the request,\ Please reopen this page and try again.", 401 ) ) isValidEmail(email) new_employee = Employees( id=id, companyId=companyId, fullname=fullname, # mobile=mobile, email=email, ) db.session.add(new_employee) db.session.commit() # once you have created an admin as an employee # let create them as user in the system. isValidPassword(password) new_user = User( id=id, username=user_data["fullname"].split(" ")[0] + "." + id, email=email, password=password, role=role, companyId=companyId, isActive=isActive, ) db.session.add(new_user) db.session.commit() return custom_make_response( "data", "Registration completed Successfully,\ you can now signin & start using the system.", 201, ) except Exception as e: message = str(e) if "UniqueViolation" and "Employees_mobile_key" in message: abort( custom_make_response( "error", "The mobile number you have entered seems\ to have been registered to another user,\ please change and try again. ", 409, ) ) elif "Employees_email_key" and "UniqueViolation" in message: abort( custom_make_response( "error", "The email address you have entered seems\ to have been registered to another user,\ please change and try again. ", 409, ) ) else: abort( custom_make_response( "error", message, 500, ) )
def create_new_project(user): """ create new project only the admin can create projects """ try: data = request.get_json() current_user = User.query.filter_by(id=user["id"]).first() _data = user_schema.dump(current_user) companyId = _data["companyId"] projectName = data["project_name"] + "." + companyId dateFrom = data["date_from"] id = generate_db_ids() project_data = ProjectNumber.query.\ filter_by(companyId=user['companyId']).first() the_number = project_number_schema.dump(project_data) project_number = the_number['projectNumber'] check_for_whitespace( data, ["project_name", "companyId", "dateFrom"]) if Project.query.filter_by(project_name=projectName).first(): abort(409) new_project = Project( id=id, project_name=projectName, companyId=companyId, date_from=dateFrom, project_status="Active", projectNumber=project_number ) db.session.add(new_project) update_project_number = { "projectNumber": project_number + 1 } ProjectNumber.query.filter_by(companyId=companyId).\ update(update_project_number) db.session.commit() return custom_make_response( "data", f"Project {projectName.split('.', 1)[0]} created successfully.", 201 ) except Exception as e: # exceptions go to site administrator log and email # the user gets a friendly error notification if (e.code == 409): return custom_make_response( "error", """ You already have another project in that name, Please change and try again ! """, 409, ) else: return custom_make_response( "error", f"{e} One or more mandatory fields has not been filled!", 400 )
def signup_system_users(): """ signup system users """ try: user_data = request.get_json() role = user_data["role"] if role == "Admin": this_company = Company.query.filter_by( company=user_data["company"]).first() _company = company_schema.dump(this_company) companyId = _company["id"] password = user_data["password"] else: companyId = user_data["companyId"] password = generate_random_password() email = user_data["email"] isActive = user_data["isActive"] this_employee = (Employees.query.filter_by( email=user_data["email"]).filter_by( companyId=user_data["companyId"]).first()) employee = employee_schema.dump(this_employee) id = employee["id"] username = employee["fullname"].split(" ")[0] + "." + id # check data for sanity incase it bypass js on the frontend check_for_whitespace( user_data, ["companyId", "username", "email", "password", "role", "status"]) isValidEmail(email) # check if user is already registered if User.query.filter_by(email=user_data["email"]).first(): abort(409) isValidPassword(password) new_user = User( id=id, username=username, email=email, password=password, companyId=companyId, role=role, isActive=isActive, ) db.session.add(new_user) db.session.commit() if role != "Admin": token = jwt.encode( { "id": id, "email": user_data["email"], "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30) }, KEY, algorithm="HS256", ) subject = """Activate your account.""" content = f""" Hey {username.split('.', 1)[0]}, {non_admin_user_registration_content()} <a href="{password_reset_url}?u={token.decode('utf-8')}" style="{button_style()}">Activate account</a> {email_signature()} """ send_mail(email, subject, content) return custom_make_response( "data", f"User registered successfully, email sent to {email}\ for further instructions.", 201, ) except Exception as e: message = str(e) if "id" in message: return custom_make_response( "error", "The user you are creating an account for\ is not on your company masterfile,\ Please add them and try again.", 400, ) elif (e.code == 409): return custom_make_response( "error", "A user account with that email already exists,\ please use another one and try again.", 409, ) else: return custom_make_response( "error", "Bummer an internal server error occured\ site admin has been notified, please give\ it a moment and try again.", 500)
def signin_all_users(): """ this signs in all users """ try: user_data = request.get_json() email = user_data["email"] password = user_data["password"] # check data for sanity incase it bypass js on the frontend check_for_whitespace(user_data, ["email", "password"]) isValidEmail(email) user = User.query.filter_by(email=user_data["email"]).first() if not user: abort(401) _user = user_schema.dump(user) _password_hash = _user["password"] if not User.compare_password(_password_hash, password): abort(401) _curr_user = user_schema.dump(user) if _curr_user["isActive"] != "true": abort(403) token = jwt.encode( { "id": _curr_user["id"], "role": _curr_user["role"], "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=480), }, KEY, algorithm="HS256", ) resp = custom_make_response( "data", { "message": "Signed in successfully, \ preparing your dashboard...", "auth_token": token.decode('utf-8'), "username": _curr_user["username"], "role": _curr_user["role"], "companyId": _curr_user["companyId"] }, 200) return resp except Exception as e: if (e.code == 401): return custom_make_response( "error", "Incorrect email and or password, check & try again !", 401) elif (e.code == 403): return custom_make_response( "error", "Your account is not in active\ status, contact company admin.", 403, ) elif (e.code == 400): return custom_make_response( "error", "One or more mandatory fields has not been filled.", 400) else: return custom_make_response( "error", "Bummer an internal server error has occured,\ the site admin has been notified, Please give it a \ moment and try again.", 500)
def forgot_password(): """send reset password email""" try: user_data = request.get_json() email = user_data["email"] check_for_whitespace(user_data, ["email"]) isValidEmail(email) user = User.query.filter_by(email=user_data["email"]).first() if not user: # well this is interesting we are aborting with a code # 200 normally this is not the case but for this one we # have to make an exception reason being we don't # want to allow enumeration attacks on our system so we # we want to make it like we sending the email even though # that will not always be the case. abort(200) this_user = user_schema.dump(user) token = jwt.encode( { "id": this_user["id"], "email": this_user["email"], "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30), }, KEY, algorithm="HS256", ) subject = """Password reset request""" content = f""" Hey {this_user['username'].split('.', 1)[0]}, {password_reset_request_content()} <a href="{password_reset_url}?u={token.decode('utf-8')}" style="{button_style()}" >Reset Password</a> {email_signature()} """ send_mail(email, subject, content) resp = custom_make_response( "data", { "message": "An email has been sent to the address on record,\ If you don't receive one shortly, please contact\ the site admin.", }, 202) return resp except Exception as e: # exceptions go to site administrator and email # the user gets a friendly error notification if (e.code == 200): return custom_make_response( "data", { "message": "An email has been sent to the address on record,\ If you don't receive one shortly, please contact\ the site admin.", }, 200) elif (e.code == 400): return custom_make_response( "error", "Please enter an email and try again.", 400) else: return custom_make_response( "error", "Bummer an internal server error has occured\ the site admin has been notified, Please\ give it some moment and try again.", 500)
def company_signup_intent(): """ send a sign up link on the email to would be customers """ try: data = request.get_json() email = data["email"] company = data["company"] id = generate_db_ids() check_for_whitespace(data, ["email", "company"]) if not (email and company): abort(400) isValidEmail(email) if Company.query.filter_by(company=data["company"]).first(): abort(409) token = jwt.encode( { "email": email, "company": company, "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30), }, KEY, algorithm="HS256", ) # send signup intent email subject = f"""Thank you for requesting to register {company}.""" content = f""" Welcome , <br/> <br/> We are grateful to have you.<br/> Please click on register below to register your personal information to start using the system.<br/>Kindly note this link will only be active for thirty minutes. <br/> <br/> <a href="{signup_url}?in={token.decode('utf-8')}" style="{button_style()}">Register</a> <br/> <br/> Regards Antony,<br/> RMS Admin. """ new_company = Company(id=id, company=company, joined_at=datetime.datetime.utcnow()) db.session.add(new_company) db.session.commit() send_mail(email, subject, content) resp = custom_make_response( "data", { "message": f"Link to register {company} sent \ successfully,head over to {email} inbox for instructions", "admin_token": token.decode('utf-8'), "company": { "company": company, "email": email } }, 201) return resp except Exception as e: error = "" if (e.code == 409): error = custom_make_response( "error", "Your company is already registered, please\ contact the company admin.", 409, ) elif (e.code == 404): error = custom_make_response( "error", "Please enter a valid email address.", 404) elif (e.code == 400): error = custom_make_response( "error", "One or more mandatory fields has not been filled.", 400) return error
def signup(): """ Sign a user up """ try: data = request.get_json() firstname = data['firstname'] lastname = data['lastname'] username = data["username"] othername = data.get("othername", "") email = data["email"] phone = data["phone"] # doesnt have to fail because of absence of this value passportUrl = data.get("passportUrl", "") password = data["password"] retypedpassword = data["retypedpassword"] except: return abort( utils.response_fn( 400, "error", 'Check your json keys. ' 'username, firstname, lastname,' 'phone, email, password')) utils.check_for_strings(data, [ "firstname", "lastname", "username", "othername", "passportUrl", "email", "phone" ]) utils.check_for_whitespace( data, ["firstname", "lastname", "username", "email", "phone"]) utils.check_for_bools(data, ["isAdmin", "isPolitician"]) # check the passwords. v2utils.doPasswordsMatch(password, retypedpassword) # check the email provided v2utils.isEmailValid(email) # Check if phone number is valid v2utils.is_phone_number_valid(phone) v2utils.check_matching_items_in_db_table({"username": username}, "users") v2utils.check_matching_items_in_db_table({"email": email}, "users") newuser = UserModel(username=username, email=email, password=password, firstname=firstname, lastname=lastname, phone=phone, passportUrl=passportUrl, othername=othername) newuser.save_user() token = jwt.encode({ "email": email, "isAdmin": False }, KEY, algorithm='HS256') return utils.response_fn(201, "data", [{ "user": { "email": newuser.email, "username": newuser.username }, "token": token.decode('UTF-8') }])