示例#1
0
def test_full_access_to_name_request(test_name, \
                                    name_request_number, temp_request_number, user_email, user_phone, \
                                    header_name_request_number, header_temp_request_number, header_user_email, header_user_phone, \
                                    expected):
    """Assure that this contains the headers required to fully access an NR."""
    from namex.utils.auth import full_access_to_name_request

    # setup
    nr = RequestDAO()
    nr.nrNum = name_request_number or temp_request_number
    nr.stateCd = State.DRAFT
    nr._source = ValidSources.NAMEREQUEST.value
    applicant = Applicant()
    applicant.phoneNumber = user_phone
    applicant.emailAddress = user_email
    nr.applicants.append(applicant)
    nr.save_to_db()

    builder = EnvironBuilder(method='POST',
                             data={},
                             headers={
                                 'BCREG_NR': header_name_request_number,
                                 'BCREG_NRL': header_temp_request_number,
                                 'BCREG-User-Email': header_user_email,
                                 'BCREG-User-Phone': header_user_phone
                             })
    env = builder.get_environ()
    req = Request(env)

    print(req)

    assert expected == full_access_to_name_request(req)
示例#2
0
    def patch(self, nr_id, action):
        """
        Roll back a Name Request to a usable state in case of a frontend error.
        :param nr_id:
        :param action:
        :return:
        """
        try:
            if not full_access_to_name_request(request):
                return {
                    "message": "You do not have access to this NameRequest."
                }, 403

            # Find the existing name request
            nr_model = Request.query.get(nr_id)

            # Creates a new NameRequestService, validates the app config, and sets request_data to the NameRequestService instance
            self.initialize()
            nr_svc = self.nr_service

            nr_svc.nr_num = nr_model.nrNum
            nr_svc.nr_id = nr_model.id

            # This could be moved out, but it's fine here for now
            def validate_patch_request(data):
                # TODO: Validate the data payload
                # Use the NR model state as the default, as the state change may not be included in the PATCH request
                is_valid = False
                msg = ''
                # This handles updates if the NR state is 'patchable'
                if NameRequestRollbackActions.has_value(action):
                    is_valid = True
                else:
                    msg = 'Invalid rollback action'

                return is_valid, msg

            is_valid_patch, validation_msg = validate_patch_request(
                self.request_data)
            validation_msg = validation_msg if not len(
                validation_msg) > 0 else 'Invalid request for PATCH'

            if not is_valid_patch:
                raise InvalidInputError(message=validation_msg)

            # This handles updates if the NR state is 'patchable'
            nr_model = self.handle_patch_rollback(nr_model, action)

            current_app.logger.debug(nr_model.json())
            response_data = nr_model.json()
            # Add the list of valid Name Request actions for the given state to the response
            response_data['actions'] = nr_svc.current_state_actions
            return jsonify(response_data), 200

        except NameRequestException as err:
            return handle_exception(err, err.message, 500)
        except Exception as err:
            return handle_exception(err, repr(err), 500)
    def get(self, nr_id):
        try:
            if not full_access_to_name_request(request):
                return {
                    "message": "You do not have access to this NameRequest."
                }, 403

            nr_model = Request.query.get(nr_id)
            if not nr_model:
                return jsonify(message='{nr_id} not found'.format(
                    nr_id=nr_model.id)), HTTPStatus.NOT_FOUND

            if nr_model.stateCd not in [
                    State.APPROVED, State.CONDITIONAL, State.CONSUMED,
                    State.EXPIRED, State.REJECTED
            ]:
                return jsonify(message='Invalid NR state'.format(
                    nr_id=nr_model.id)), HTTPStatus.BAD_REQUEST

            authenticated, token = ReportResource._get_service_client_token()
            if not authenticated:
                return jsonify(message='Error in authentication'.format(nr_id=nr_model.id)),\
                       HTTPStatus.INTERNAL_SERVER_ERROR

            headers = {
                'Authorization': 'Bearer {}'.format(token),
                'Content-Type': 'application/json'
            }
            data = {
                'reportName':
                ReportResource._get_report_filename(nr_model),
                'template':
                "'" + base64.b64encode(bytes(self._get_template(),
                                             'utf-8')).decode() + "'",
                'templateVars':
                ReportResource._get_template_data(nr_model)
            }
            response = requests.post(
                url=current_app.config.get('REPORT_SVC_URL'),
                headers=headers,
                data=json.dumps(data))

            if response.status_code != HTTPStatus.OK:
                return jsonify(
                    message=str(response.content)), response.status_code
            return response.content, response.status_code
        except Exception as err:
            return handle_exception(err, 'Error retrieving the report.', 500)
示例#4
0
    def put(self, nr_id):
        """
        NOT used for Existing Name Request updates that only change the Name Request. Use 'patch' instead.
        State changes handled include state changes to [DRAFT, COND_RESERVE, RESERVED, COND_RESERVE to CONDITIONAL, RESERVED to APPROVED]
        :param nr_id:
        :return:
        """
        try:
            if not full_access_to_name_request(request):
                return {
                    "message": "You do not have access to this NameRequest."
                }, 403
            # Find the existing name request
            nr_model = Request.query.get(nr_id)

            # Creates a new NameRequestService, validates the app config, and sets request_data to the NameRequestService instance
            self.initialize()
            nr_svc = self.nr_service

            nr_svc.nr_num = nr_model.nrNum
            nr_svc.nr_id = nr_model.id

            valid_update_states = [
                State.DRAFT, State.COND_RESERVE, State.RESERVED,
                State.PENDING_PAYMENT
            ]

            # This could be moved out, but it's fine here for now
            def validate_put_request(data):
                is_valid = False
                msg = ''
                if data.get('stateCd') in valid_update_states:
                    is_valid = True

                return is_valid, msg

            is_valid_put, validation_msg = validate_put_request(
                self.request_data)
            validation_msg = validation_msg if not len(
                validation_msg) > 0 else 'Invalid request for PUT'

            if not is_valid_put:
                raise InvalidInputError(message=validation_msg)

            if nr_model.stateCd in valid_update_states:
                nr_model = self.update_nr(nr_model, nr_model.stateCd,
                                          self.handle_nr_update)

                # Record the event
                EventRecorder.record(nr_svc.user, Event.PUT, nr_model,
                                     nr_svc.request_data)

            current_app.logger.debug(nr_model.json())
            response_data = nr_model.json()
            # Add the list of valid Name Request actions for the given state to the response
            response_data['actions'] = nr_svc.current_state_actions
            return jsonify(response_data), 200
        except NameRequestException as err:
            return handle_exception(err, err.message, 500)
        except Exception as err:
            return handle_exception(err, repr(err), 500)
示例#5
0
    def patch(self, nr_id, nr_action: str):
        """
        Update a specific set of fields and/or a provided action. Fields excluded from the payload will not be updated.
        The following data format is expected when providing a data payload:
        { 'stateCd': 'CANCELLED' }  # Fields to update

        We use this to:
        - Edit a subset of NR fields
        - Cancel an NR
        - Change the state of an NR
        :param nr_id:
        :param nr_action: One of [CHECKOUT, CHECKIN, EDIT, CANCEL, RESEND]
        :return:
        """
        try:
            if not full_access_to_name_request(request):
                return {
                    "message": "You do not have access to this NameRequest."
                }, 403

            nr_action = str(nr_action).upper(
            )  # Convert to upper-case, just so we can support lower case action strings
            nr_action = NameRequestPatchActions[nr_action].value \
                if NameRequestPatchActions.has_value(nr_action) \
                else NameRequestPatchActions.EDIT.value

            # Find the existing name request
            nr_model = Request.query.get(nr_id)

            def initialize(_self):
                _self.validate_config(current_app)
                request_json = request.get_json()

                if nr_action:
                    _self.nr_action = nr_action

                if nr_action is NameRequestPatchActions.CHECKOUT.value:
                    # Make sure the NR isn't already checked out
                    checked_out_by_different_user = nr_model.checkedOutBy is not None and nr_model.checkedOutBy != request_json.get(
                        'checkedOutBy', None)
                    if checked_out_by_different_user:
                        raise NameRequestIsInProgressError()

                    # set the user id of the request to name_request_service_account
                    service_account_user = User.find_by_username(
                        'name_request_service_account')
                    nr_model.userId = service_account_user.id

                    # The request payload will be empty when making this call, add them to the request
                    _self.request_data = {
                        # Doesn't have to be a UUID but this is easy and works for a pretty unique token
                        'checkedOutBy': str(uuid4()),
                        'checkedOutDt': datetime.now()
                    }
                    # Set the request data to the service
                    _self.nr_service.request_data = self.request_data
                elif nr_action is NameRequestPatchActions.CHECKIN.value:
                    # The request payload will be empty when making this call, add them to the request
                    _self.request_data = {
                        'checkedOutBy': None,
                        'checkedOutDt': None
                    }
                    # Set the request data to the service
                    _self.nr_service.request_data = self.request_data
                else:
                    super().initialize()

            initialize(self)

            nr_svc = self.nr_service
            nr_svc.nr_num = nr_model.nrNum
            nr_svc.nr_id = nr_model.id

            # This could be moved out, but it's fine here for now
            def validate_patch_request(data):
                # Use the NR model state as the default, as the state change may not be included in the PATCH request
                request_state = data.get('stateCd', nr_model.stateCd)
                is_valid = False
                msg = ''

                # Handles updates if the NR state is 'patchable'
                if request_state in request_editable_states:
                    is_valid = True
                elif request_state in contact_editable_states:
                    is_valid = True
                else:
                    msg = 'Invalid state change requested - the Name Request state cannot be changed to [' + data.get(
                        'stateCd', '') + ']'

                # Check the action, make sure it's valid
                if not NameRequestPatchActions.has_value(nr_action):
                    is_valid = False
                    msg = 'Invalid Name Request PATCH action, please use one of [' + ', '.join(
                        [action.value
                         for action in NameRequestPatchActions]) + ']'
                return is_valid, msg

            is_valid_patch, validation_msg = validate_patch_request(
                self.request_data)
            validation_msg = validation_msg if not len(
                validation_msg) > 0 else 'Invalid request for PATCH'

            if not is_valid_patch:
                raise InvalidInputError(message=validation_msg)

            def handle_patch_actions(action, model):
                return {
                    NameRequestPatchActions.CHECKOUT.value:
                    self.handle_patch_checkout,
                    NameRequestPatchActions.CHECKIN.value:
                    self.handle_patch_checkin,
                    NameRequestPatchActions.EDIT.value:
                    self.handle_patch_edit,
                    NameRequestPatchActions.CANCEL.value:
                    self.handle_patch_cancel,
                    NameRequestPatchActions.RESEND.value:
                    self.handle_patch_resend,
                    NameRequestPatchActions.REQUEST_REFUND.value:
                    self.handle_patch_request_refund
                }.get(action)(model)

            # This handles updates if the NR state is 'patchable'
            nr_model = handle_patch_actions(nr_action, nr_model)

            current_app.logger.debug(nr_model.json())
            response_data = nr_model.json()

            # Don't return the whole response object if we're checking in or checking out
            if nr_action == NameRequestPatchActions.CHECKOUT.value:
                response_data = {
                    'id': nr_id,
                    'checkedOutBy': response_data.get('checkedOutBy'),
                    'checkedOutDt': response_data.get('checkedOutDt'),
                    'state': response_data.get('state', ''),
                    'stateCd': response_data.get('stateCd', ''),
                    'actions': nr_svc.current_state_actions
                }
                return jsonify(response_data), 200

            if nr_action == NameRequestPatchActions.CHECKIN.value:
                response_data = {
                    'id': nr_id,
                    'state': response_data.get('state', ''),
                    'stateCd': response_data.get('stateCd', ''),
                    'actions': nr_svc.current_state_actions
                }
                return jsonify(response_data), 200

            # Add the list of valid Name Request actions for the given state to the response
            if (nr_action == NameRequestPatchActions.REQUEST_REFUND.value):
                response_data['actions'] = []
            else:
                response_data['actions'] = nr_svc.current_state_actions
            return jsonify(response_data), 200

        except NameRequestIsInProgressError as err:
            # Might as well use the Mozilla WebDAV HTTP Locked status, it's pretty close
            return handle_exception(err, err.message, 423)
        except NameRequestException as err:
            return handle_exception(err, err.message, 500)
        except Exception as err:
            return handle_exception(err, repr(err), 500)
示例#6
0
    def get(self):
        try:
            if not full_access_to_name_request(request):
                return {"message": "You do not have access to this NameRequest."}, 403

            filters = []

            nr_num_query_str = request.headers['Bcreg-Nr'] or request.headers['Bcreg-Nrl']
            email_address_query_str = request.headers['Bcreg-User-Email']
            phone_number_query_str = request.headers['Bcreg-User-Phone']

            if not nr_num_query_str:
                raise InvalidInputError(message='An nrNum must be provided')
            else:
                if not email_address_query_str and not phone_number_query_str:
                    raise InvalidInputError(message='Either an emailAddress or phoneNumber must be provided')

            # Continue
            nr_num = parse_nr_num(nr_num_query_str)
            email_address = email_address_query_str
            phone_number = phone_number_query_str
            # Filter on addresses
            # address_line = get_query_param_str('addrLine1')

            if nr_num:
                filters.append(func.lower(Request.nrNum) == nr_num.lower())
            if phone_number:
                strip_phone_number_chars_regex = r"[^0-9]"
                filters.append(
                    Request.applicants.any(
                        func.regexp_replace(Applicant.phoneNumber, strip_phone_number_chars_regex, '', 'g').contains(re.sub(strip_phone_number_chars_regex, '', phone_number))
                    )
                )

            if email_address:
                filters.append(
                    Request.applicants.any(
                        func.lower(Applicant.emailAddress).startswith(email_address.lower())
                    )
                )

            '''
            Filter on addresses
            if address_line:
                filters.append(
                    Request.applicants.any(
                        func.lower(Applicant.addrLine1).startswith(address_line.lower())
                    )
                )
            '''

            criteria = RequestQueryCriteria(
                nr_num=nr_num,
                filters=filters
            )

            results = Request.find_by_criteria(criteria)

            if not results:
                results = []

        except InvalidInputError as err:
            return handle_exception(err, err.message, 400)
        except Exception as err:
            return handle_exception(err, 'Error retrieving the NR from the db.', 500)

        if nr_num and len(results) == 1:
            nr_model = results[0]

            if nr_model.requestTypeCd and (not nr_model.entity_type_cd or not nr_model.request_action_cd):
                # If requestTypeCd is set, but a request_entity (entity_type_cd) and a request_action (request_action_cd)
                # are not, use get_mapped_entity_and_action_code to map the values from the requestTypeCd
                entity_type, request_action = get_mapped_entity_and_action_code(nr_model.requestTypeCd)
                nr_model.entity_type_cd = entity_type
                nr_model.request_action_cd = request_action

            response_data = nr_model.json()

            # If draft, get the wait time and oldest queued request
            if nr_model.stateCd == 'DRAFT':
                service = WaitTimeStatsService()
                wait_time_response = service.get_waiting_time_dict()
                response_data.update(wait_time_response)

            # Add the list of valid Name Request actions for the given state to the response
            response_data['actions'] = get_nr_state_actions(results[0].stateCd, results[0])
            return jsonify(response_data), 200
        elif len(results) > 0:
            # We won't add the list of valid Name Request actions for the given state to the response if we're sending back a list
            # If the user / client accessing this data needs the Name Request actions, GET the individual record using NameRequest.get
            # This method, NameRequests.get is for Existing NR Search
            return jsonify(list(map(lambda result: result.json(), results))), 200

        # We won't add the list of valid Name Request actions for the given state to the response if we're sending back a list
        # If the user / client accessing this data needs the Name Request actions, GET the individual record using NameRequest.get
        # This method, NameRequests.get is for Existing NR Search
        return jsonify(results), 200