def update_active_task(event: HTTPEvent) -> JSONResponse: sub = event.params['sub'] schema = Schema({ 'description': str, 'sub-tasks': [{ 'description': str, 'completed': bool }] }) try: body = schema.validate(event.json) except SchemaError as e: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, str(e)) if event.authorizer.sub != sub and not event.authorizer.is_scouter: return JSONResponse.generate_error( HTTPError.FORBIDDEN, "You have no access to this resource with this user") return JSONResponse({ 'message': 'Updated active task', 'active-task': TasksService.update_active_task(event.authorizer, body['description'], body['sub-tasks']) })
def start_task(event: HTTPEvent) -> JSONResponse: sub = event.params['sub'] stage = event.params['stage'] area = event.params['area'] subline = event.params['subline'] schema = Schema({'description': str, 'sub-tasks': [str]}) try: body = schema.validate(event.json) except SchemaError as e: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, str(e)) if event.authorizer.sub != sub: return JSONResponse.generate_error( HTTPError.FORBIDDEN, "You have no access to this resource with this user") try: task = TasksService.start_task(event.authorizer, stage, area, subline, body['sub-tasks'], body['description']) if task is None: return JSONResponse.generate_error( HTTPError.INVALID_CONTENT, 'An active task already exists') except NotFoundException: JSONResponse.generate_error(HTTPError.NOT_FOUND, 'Objective not found') return JSONResponse({'message': 'Started new task'})
def fetch_user_tasks(event: HTTPEvent) -> JSONResponse: sub = event.params.get('sub') stage = event.params.get('stage') area = event.params.get('area') if stage is not None and stage not in VALID_STAGES: return JSONResponse.generate_error(HTTPError.NOT_FOUND, f"Stage {stage} not found") if area is not None and area not in VALID_AREAS: return JSONResponse.generate_error(HTTPError.NOT_FOUND, f"Area {area} not found") line_key: Optional[str] = event.params.get('subline') if line_key is not None: lines = line_key.split('.') if len(lines) != 2: return JSONResponse.generate_error( HTTPError.NOT_FOUND, f"Subline {line_key} not valid") line, subline = lines try: line = int(line) subline = int(subline) except ValueError: return JSONResponse.generate_error( HTTPError.NOT_FOUND, f"Subline {line_key} not valid") result = TasksService.get(sub, stage, area, line, subline).to_api_dict() else: result = TasksService.query(sub, stage, area).as_dict(lambda t: t.to_api_dict()) return JSONResponse(result)
def signup_beneficiary(event: HTTPEvent): data = json.loads(event.body) try: attrs = { 'name': data['name'], 'family_name': data['family_name'], 'birthdate': data['birthdate'], 'gender': data['unit'], 'nickname': data['nickname'] } try: datetime.strptime(attrs["birthdate"], "%d-%m-%Y") except ValueError: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, "Invalid date format, it must match " "%d-%m-%Y format") middle_name = data.get('middle_name') if middle_name is not None: attrs['middle_name'] = middle_name UsersCognito.sign_up(data['email'], data['password'], attrs) except UsersCognito.get_client().exceptions.UsernameExistsException: return JSONResponse.generate_error(HTTPError.EMAIL_ALREADY_IN_USE, "E-mail already in use") except UsersCognito.get_client().exceptions.InvalidPasswordException: return JSONResponse.generate_error(HTTPError.EMAIL_ALREADY_IN_USE, "Invalid password. Password must have " "uppercase, lowercase, numbers and be at " "least 6 characters long") except ParamValidationError as e: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, str(e)) UsersCognito.add_to_group(data['email'], "Beneficiaries") return JSONResponse({"message": "OK"})
def complete_active_task(event: HTTPEvent) -> JSONResponse: sub = event.params['sub'] if event.authorizer.sub != sub and not event.authorizer.is_scouter: return JSONResponse.generate_error( HTTPError.FORBIDDEN, "You have no access to this resource with this user") completed_task = TasksService.complete_active_task(event.authorizer) if completed_task is None: return JSONResponse.generate_error(HTTPError.NOT_FOUND, "No active task found") area = split_key(completed_task['objective'])[1] response = JSONResponse({ 'message': 'Completed task', 'task': completed_task, 'reward': RewardsFactory.get_reward_token_by_reason( authorizer=event.authorizer, area=area, reason=RewardReason.COMPLETE_OBJECTIVE), }) LogsService.create( event.authorizer.sub, LogTag.COMPLETED.join(completed_task['objective'].upper()), 'Completed an objective!', {}) return response
def confirm_user(event: HTTPEvent): data = json.loads(event.body) try: return UsersCognito.confirm(data['email'], data['code']) except UsersCognito.get_client().exceptions.UserNotFoundException: return JSONResponse.generate_error(HTTPError.UNKNOWN_USER, "User not found") except ParamValidationError as e: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, str(e))
def update_beneficiary(event: HTTPEvent): if event.authorizer.sub != event.params["sub"]: return JSONResponse.generate_error(HTTPError.FORBIDDEN, "You can not access data from this beneficiary") result = BeneficiariesService.update(event.authorizer, profile_picture=event.json.get('profile_picture'), nickname=event.json.get('nickname')) if result is None: return JSONResponse.generate_error(HTTPError.NOT_FOUND, "This user does not have a beneficiaries assigned") return JSONResponse({"message": "Updated successfully"})
def confirm(cls, username: str, code: str): client = cls.get_client() try: client.confirm_sign_up(ClientId=cls.get_client_id(), Username=username, ConfirmationCode=code) return JSONResponse({"message": "Confirmed account"}) except client.exceptions.CodeMismatchException: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, "Wrong confirmation code") except client.exceptions.NotAuthorizedException: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, "Already confirmed")
def handler(event: dict, _) -> dict: event = HTTPEvent(event) if event.method == "GET": result = get_handler(event) elif event.method == "POST": if event.resource == "/api/auth/scouters-signup": result = signup_scouter(event) else: result = JSONResponse.generate_error(HTTPError.UNKNOWN_RESOURCE, f"Resource {event.resource} unknown") else: result = JSONResponse.generate_error(HTTPError.NOT_IMPLEMENTED, f"Method {event.method} is not valid") return result.as_dict()
def refresh_token(event: HTTPEvent): data = json.loads(event.body) try: token = UsersCognito.refresh(data['token']) if token is None: return JSONResponse.generate_error(HTTPError.FORBIDDEN, "Invalid credentials") return JSONResponse({ "message": "Refresh successful", "token": token.as_dict() }) except UsersCognito.get_client().exceptions.UserNotFoundException: return JSONResponse.generate_error(HTTPError.UNKNOWN_USER, "User not found") except ParamValidationError as e: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, str(e))
def create_item(event: HTTPEvent): category = event.params['category'] if not event.authorizer.is_admin: raise ForbiddenException( "This endpoint can only be accessed by an administrator") try: release = int(event.params['release']) except ValueError: return JSONResponse.generate_error( HTTPError.INVALID_CONTENT, f"Invalid release {event.params['release']}, it should be an int") body = event.json body['category'] = category body['release'] = release reward = Reward.from_api_map(body) result = RewardsService.create(reward.description, reward.type, reward.release, reward.rarity, reward.price) reward = Reward.from_db_map(result.item) return JSONResponse({ 'message': 'Created item', 'item': reward.to_api_map() })
def update_avatar(event: HTTPEvent) -> JSONResponse: sub = event.params["sub"] if event.authorizer.sub != sub: return JSONResponse.generate_error( HTTPError.FORBIDDEN, "Only the avatar owner can update the avatar") new_avatar = BeneficiariesService.update_avatar(event.authorizer.sub, event.json) return JSONResponse({'avatar': new_avatar})
def route(self, event: HTTPEvent) -> JSONResponse: resources = self.routes.get(event.method) if resources is None: return JSONResponse.generate_error(HTTPError.UNKNOWN_RESOURCE, f"Unknown method {event.method}") resource = self.standardize_resource(event.resource) fun = resources.get(resource) if fun is None: return JSONResponse.generate_error(HTTPError.UNKNOWN_RESOURCE, f"Unknown resource {event.resource}") try: return fun(event) except ForbiddenException as e: return JSONResponse.generate_error(HTTPError.FORBIDDEN, e.message) except NotFoundException as e: return JSONResponse.generate_error(HTTPError.NOT_FOUND, e.message) except InvalidException as e: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, e.message) except UnauthorizedException as e: return JSONResponse.generate_error(HTTPError.UNAUTHORIZED, e.message) except SchemaError as e: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, f"Bad schema: {e}") except Exception as e: body = { "code": HTTPError.SERVER_ERROR.name, "message": "An unknown error ocurred" } error = { "type": str(type(e)), "args": str(e.args), "traceback": [f.strip() for f in traceback.format_tb(e.__traceback__)] } if event.authorizer is not None and event.authorizer.is_admin: body["error"] = error return JSONResponse(body, 500)
def get_beneficiary(event: HTTPEvent): result = BeneficiariesService.get(event.params["sub"]) if result is None: return JSONResponse.generate_error(HTTPError.NOT_FOUND, "This user does not have a beneficiaries assigned") has_full_access = event.authorizer is not None and ( event.authorizer.is_scouter or event.authorizer.sub == event.params["sub"] ) return JSONResponse(result.to_api_dict(full=has_full_access))
def handler(event: dict, _) -> dict: event = HTTPEvent(event) if event.method == "POST": if event.resource == "/api/auth/login": result = login(event) elif event.resource == "/api/auth/confirm": result = confirm_user(event) elif event.resource == "/api/auth/refresh": result = refresh_token(event) else: result = JSONResponse.generate_error( HTTPError.UNKNOWN_RESOURCE, f"Resource {event.resource} unknown") else: result = JSONResponse.generate_error( HTTPError.NOT_IMPLEMENTED, f"Method {event.method} is not valid") return result.as_dict()
def get_item(event: HTTPEvent): category = event.params['category'] try: release = int(event.params['release']) except ValueError: return JSONResponse.generate_error( HTTPError.NOT_FOUND, f"Unknown release {event.params['release']}, it should be an int") try: id_ = int(event.params['id']) except ValueError: return JSONResponse.generate_error( HTTPError.NOT_FOUND, f"Unknown id {event.params['id']}, it should be an int") return JSONResponse(RewardsService.get(category, release, id_).as_dict())
def list_beneficiaries_unit(event: HTTPEvent): district = event.params["district"] group = event.params["group"] unit = event.params["unit"] if unit not in VALID_UNITS: return JSONResponse.generate_error(HTTPError.NOT_FOUND, f"Unknown unit: {unit}") result = BeneficiariesService.query_unit(district, group, unit) result.items = [item.to_api_dict() for item in result.items] return JSONResponse(result.as_dict())
def login(event: HTTPEvent): data = json.loads(event.body) try: token = UsersCognito.log_in(data['email'], data['password']) if token is None: return JSONResponse.generate_error(HTTPError.FORBIDDEN, "Invalid credentials") return JSONResponse({ "message": "Log-in successful", "token": token.as_dict() }) except UsersCognito.get_client().exceptions.UserNotConfirmedException: return JSONResponse.generate_error( HTTPError.UNCONFIRMED_USER, "User is unconfirmed, check your e-mail for " "the confirmation code.") except ParamValidationError as e: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, e.message)
def signup_scouter(event: HTTPEvent): data = json.loads(event.body) try: UsersCognito.sign_up(data['email'], data['password'], { 'name': data['name'], 'middle_name': data.get('middle_name'), 'family_name': data['family_name'] }) except UsersCognito.get_client().exceptions.UsernameExistsException: return JSONResponse.generate_error(HTTPError.EMAIL_ALREADY_IN_USE, "E-mail already in use") except UsersCognito.get_client().exceptions.InvalidPasswordException: return JSONResponse.generate_error(HTTPError.EMAIL_ALREADY_IN_USE, "Invalid password. Password must have " "uppercase, lowercase, numbers and be at " "least 6 characters long") except ParamValidationError as e: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, str(e)) UsersCognito.add_to_group(data['email'], "Scouters") return JSONResponse({"message": "OK"})
def get_handler(event: HTTPEvent): district = event.params["district"] group = event.params["group"] code = event.params.get("code") if code is None: result = get_scouters(district, group, event) else: result = get_scouter(district, group, code, event) if result is None: return JSONResponse.generate_error(HTTPError.NOT_FOUND, "Scouter not found") return JSONResponse(result)
def list_shop_category(event: HTTPEvent): category = event.params['category'] try: release = int(event.params['release']) except ValueError: return JSONResponse.generate_error( HTTPError.INVALID_CONTENT, f"Invalid release {event.params['release']}, it should be an int") return JSONResponse( RewardsService.query(RewardType.from_value(category.upper()), release).as_dict())
def get_handler(event: HTTPEvent) -> JSONResponse: # validate unit unit = event.params.get("unit").lower() if unit not in VALID_UNITS: return JSONResponse.generate_error(HTTPError.NOT_FOUND, f"Unit '{unit}' is not valid") # validate stage stage = event.params.get("stage").lower() if stage not in VALID_STAGES: return JSONResponse.generate_error(HTTPError.NOT_FOUND, f"Stage '{stage}' not found") area = event.params.get("area") line = event.params.get("line") if area is None and line is None: # get all objectives from unit and stage response = get_objectives(stage) else: # get one objective area = area.lower() if area not in VALID_AREAS: return JSONResponse.generate_error(HTTPError.NOT_FOUND, f"Area '{area}' not found") try: line = int(line) except ValueError: return JSONResponse.generate_error( HTTPError.INVALID_ID, f"Given line ID '{line}' is not a number") response = get_objective(unit, stage, area, line) if response.item is None: return JSONResponse.generate_error( HTTPError.NOT_FOUND, f"Could not find '{area}' objective with line {line}") process_objective(response.item) return JSONResponse(response.as_dict())
def dismiss_active_task(event: HTTPEvent) -> JSONResponse: sub = event.params['sub'] if event.authorizer.sub != sub and not event.authorizer.is_scouter: return JSONResponse.generate_error( HTTPError.FORBIDDEN, "You have no access to this resource with this user") return JSONResponse({ 'message': 'Task dismissed', 'task': TasksService.dismiss_active_task(event.authorizer) })
def initialize_tasks(event: HTTPEvent) -> JSONResponse: sub = event.params['sub'] if event.authorizer.sub != sub: return JSONResponse.generate_error( HTTPError.FORBIDDEN, "You have no access to this resource with this user") body = event.json if 'objectives' not in body: return JSONResponse.generate_error(HTTPError.INVALID_CONTENT, "No objectives found") if not isinstance(body['objectives'], list): return JSONResponse.generate_error( HTTPError.INVALID_CONTENT, "Objectives must be a list of objects") objectives: List[ObjectiveKey] = [] for obj in body['objectives']: if not isinstance(obj, dict): return JSONResponse.generate_error( HTTPError.INVALID_CONTENT, "Each objective must be an object") if 'line' not in obj or not isinstance(obj['line'], int): return JSONResponse.generate_error( HTTPError.INVALID_CONTENT, "Each objective must have the key 'line' and it must be an int" ) if 'subline' not in obj or not isinstance(obj['subline'], int): return JSONResponse.generate_error( HTTPError.INVALID_CONTENT, "Each objective must have the key 'subline' and it must be an int" ) if 'area' not in obj or obj['area'] not in VALID_AREAS: return JSONResponse.generate_error( HTTPError.INVALID_CONTENT, f"Each objective must have the key 'area' and it must a valid area " f"name: {VALID_AREAS}") objectives.append( ObjectiveKey(line=obj['line'], subline=obj['subline'], area=obj['area'])) return JSONResponse({ 'message': 'Task dismissed', 'reward': TasksService.initialize(event.authorizer, objectives) })