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)
Beispiel #4
0
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
Beispiel #6
0
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))
Beispiel #7
0
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"})
Beispiel #8
0
    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")
Beispiel #9
0
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()
Beispiel #10
0
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()
    })
Beispiel #12
0
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})
Beispiel #13
0
 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)
Beispiel #14
0
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))
Beispiel #15
0
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())
Beispiel #17
0
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())
Beispiel #18
0
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)
Beispiel #19
0
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"})
Beispiel #20
0
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())
Beispiel #22
0
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)
    })