Ejemplo n.º 1
0
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()
    })
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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'])
    })
Ejemplo n.º 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"})
Ejemplo n.º 5
0
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
Ejemplo n.º 6
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})
Ejemplo n.º 7
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))
Ejemplo n.º 8
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))
Ejemplo n.º 9
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())
Ejemplo n.º 10
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"})
Ejemplo n.º 11
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)
Ejemplo n.º 12
0
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())
Ejemplo n.º 13
0
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)
    })
Ejemplo n.º 14
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()
Ejemplo n.º 15
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)
Ejemplo n.º 16
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")
Ejemplo n.º 17
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))
Ejemplo n.º 18
0
def buy_item(event: HTTPEvent):
    category = event.params['category']

    try:
        release = int(event.params['release'])
    except ValueError:
        return NotFoundException(
            f"Unknown release {event.params['release']}, it should be an int")

    try:
        id_ = int(event.params['id'])
    except ValueError:
        raise NotFoundException(
            f"Unknown id {event.params['id']}, it should be an int")

    area = event.params['area']
    if area not in VALID_AREAS:
        raise NotFoundException(f"Area {area} does not exist")

    amount = event.json.get('amount', 1)
    if type(amount) is not int:
        raise InvalidException(f"The amount to be bought must be an integer")
    if amount < 1:
        raise InvalidException(f"The amount must be one or more")

    try:
        result = BeneficiariesService.buy_item(event.authorizer, area,
                                               category, release, id_, amount)
    except BeneficiariesService.exceptions().ConditionalCheckFailedException:
        raise ForbiddenException(
            f"You don't have enough {area} score to buy this item")

    if not result:
        return NotFoundException(f"Item not found")
    return JSONResponse(result)
Ejemplo n.º 19
0
def get_district(event: HTTPEvent):
    code = event.params["district"]

    response = DistrictModel.get({"code": code})
    if response.item is None:
        raise NotFoundException(f"District '{code}' was not found")
    response.item = District.from_db(response.item).to_api_map()
    return JSONResponse(response.as_dict())
Ejemplo n.º 20
0
def get_my_rewards(event: HTTPEvent):
    category_name: str = event.params.get('category')
    category = RewardType.from_value(category_name.upper())
    return JSONResponse({
        'rewards': [
            log.to_api_map() for log in RewardsService.get_user_rewards(
                event.authorizer, category)
        ]
    })
Ejemplo n.º 21
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()
Ejemplo n.º 22
0
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())
Ejemplo n.º 23
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"})
Ejemplo n.º 24
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)
Ejemplo n.º 25
0
def query_logs(event: HTTPEvent):
    user_sub = event.params['sub']
    tag = LogTag.normalize(split_key(event.params['tag'])).upper() if event.params.get('tag') else None

    limit = event.queryParams.get('limit', 25)
    if not isinstance(limit, int) or limit > 100:
        raise InvalidException("Limit must be an integer and lower or equal than 100")

    logs = LogsService.query(user_sub, tag, limit=limit)
    return JSONResponse(body=QueryResult.from_list([log.to_api_map() for log in logs]).as_dict())
Ejemplo n.º 26
0
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'})
Ejemplo n.º 27
0
def get_user_active_task(event: HTTPEvent) -> JSONResponse:
    sub = event.params['sub']
    result = TasksService.get_active_task(sub)
    beneficiary_authorizer = event.authorizer if event.authorizer is not None and event.authorizer.sub == sub else None
    d = result.to_api_dict(authorizer=beneficiary_authorizer)
    if beneficiary_authorizer is not None:
        # generate log reward claim token
        last_task_log = LogsService.get_last_log_with_tag(
            sub,
            tag=join_key(LogTag.PROGRESS.value, result.objective_key).upper())
        d['eligible_for_progress_reward'] = last_task_log is None or int(
            time.time() * 1000) - last_task_log.timestamp > 24 * 60 * 60 * 1000

    return JSONResponse(d)
Ejemplo n.º 28
0
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)
    })
Ejemplo n.º 29
0
def create_log(event: HTTPEvent):
    user_sub: str = event.params['sub']
    tag: str = event.params['tag'].upper()

    if event.authorizer.sub != user_sub:
        raise ForbiddenException("Only the same user can create logs")
    parent_tag = LogTag.from_short(split_key(tag)[0])
    if parent_tag not in USER_VALID_TAGS:
        raise ForbiddenException(f"A user can only create logs with the following tags: {USER_VALID_TAGS}")

    body = event.json

    log = body['log']
    data = body.get('data')

    if len(body) > 1024:
        raise InvalidException(f"A log can't have more than 1024 characters")

    if data is not None:
        if len(json.dumps(data)) > 2048:
            raise InvalidException(f"Log data is too big")

    response_body = {}

    if parent_tag == LogTag.PROGRESS:
        if tag != parent_tag.short:
            raise InvalidException(f"A progress log tag can't be compound")
        if body.get('token') is None:
            raise InvalidException(f"To post a PROGRESS log you must provide the task token")

        objective = TasksService.get_task_token_objective(body['token'], authorizer=event.authorizer)
        tag = join_key(LogTag.PROGRESS.value, objective).upper()

        now = int(datetime.now(timezone.utc).timestamp() * 1000)
        last_progress_log = LogsService.get_last_log_with_tag(event.authorizer.sub, tag.upper())

        if last_progress_log is None or now - last_progress_log.timestamp > 24 * 60 * 60 * 1000:
            response_body['token'] = RewardsFactory.get_reward_token_by_reason(authorizer=event.authorizer,
                                                                               area=split_key(objective)[1],
                                                                               reason=RewardReason.PROGRESS_LOG)

    log = LogsService.create(user_sub, tag, log_text=log, data=body.get('data'), append_timestamp_to_tag=True)
    response_body['item'] = log.to_api_map()

    return JSONResponse(body=response_body)
Ejemplo n.º 30
0
def claim_reward(event: HTTPEvent):
    body = event.json
    token = body.get('token')
    if token is None:
        raise InvalidException('No reward token given')
    box_index = body.get('box_index')
    if box_index is not None:
        try:
            box_index = int(box_index)
        except ValueError:
            raise InvalidException('Box index must be an int')
    rewards = RewardsService.claim_reward(event.authorizer,
                                          reward_token=token,
                                          box_index=box_index,
                                          release=1)
    return JSONResponse({
        'message': 'Claimed rewards!',
        'rewards': [reward.to_api_map() for reward in rewards]
    })