def create_session(self):
        """
        Initializes a session with the testing service.

        """
        url = '{service_root_url}/sessions'.format(
            service_root_url=SERVICE_ROOT_URL)
        data = {'language': SERVICE_LANGUAGE}
        response = requests.post(url,
                                 data=json.dumps(data),
                                 headers={
                                     'Content-Type': 'application/json',
                                     'Accept': 'text/plain'
                                 })
        assert response.status_code == 200, 'Failed to create session. Response from testing service: {}'.format(
            response.content)

        session_id_response = response.content.decode('UTF-8')
        try:
            int(session_id_response)
        except ValueError as e:
            print(
                'Failed to create session, did not receive valid session ID from testing service: {}'
                .format(session_id_response))
            raise e

        self.session_id = session_id_response
        print('Created session: {}'.format(self.session_id))
        return self.session_id
예제 #2
0
def refresh(ctx):
    client_config = cli_util.build_config(ctx.obj)

    security_token_file = client_config.get('security_token_file')
    if not security_token_file:
        click.echo(
            "ERROR: Cannot refresh a profile that does not have a value for 'security_token_file'",
            file=sys.stderr)
        sys.exit(1)

    expanded_security_token_location = os.path.expanduser(security_token_file)
    if not os.path.exists(expanded_security_token_location):
        click.echo(
            "ERROR: Cannot refresh token, 'security_token_file' does not exist: {}"
            .format(expanded_security_token_location))
        sys.exit(1)

    with open(expanded_security_token_location, 'r') as security_token_file:
        token = security_token_file.read()

    try:
        private_key = oci.signer.load_private_key_from_file(
            client_config.get('key_file'), client_config.get('pass_phrase'))
    except oci.exceptions.MissingPrivateKeyPassphrase:
        client_config['pass_phrase'] = cli_util.prompt_for_passphrase()
        private_key = oci.signer.load_private_key_from_file(
            client_config.get('key_file'), client_config.get('pass_phrase'))
    auth = oci.auth.signers.SecurityTokenSigner(token, private_key)

    refresh_url = "{endpoint}/v1/authentication/refresh".format(
        endpoint=oci.regions.endpoint_for("auth", client_config.get('region')))
    click.echo("Attempting to refresh token from {refresh_url}".format(
        refresh_url=refresh_url),
               file=sys.stderr)

    response = requests.post(refresh_url,
                             headers={'content-type': 'application/json'},
                             data=json.dumps({'currentToken': token}),
                             auth=auth)

    if response.status_code == 200:
        refreshed_token = json.loads(response.content.decode('UTF-8'))['token']
        with open(expanded_security_token_location,
                  'w') as security_token_file:
            security_token_file.write(refreshed_token)
        cli_util.apply_user_only_access_permissions(
            expanded_security_token_location)
        click.echo("Successfully refreshed token", file=sys.stderr)
    elif response.status_code == 401:
        click.echo(
            "Your session is no longer valid and cannot be refreshed. Please use 'oci session authenticate' to create a new session.",
            file=sys.stderr)
        sys.exit(1)
    else:
        click.echo("ERROR: Failed to refresh sesison. Error: {}".format(
            str(response.content.decode('UTF-8'))))
        sys.exit(1)
예제 #3
0
def refresh(ctx):
    ctx.obj['config_file'] = cli_setup.DEFAULT_CONFIG_LOCATION

    client_config = cli_util.build_config(ctx.obj)

    security_token_file = client_config.get('security_token_file')
    if not security_token_file:
        click.echo("ERROR: Cannot refresh a profile that does not have a value for 'security_token_file'", file=sys.stderr)
        sys.exit(1)

    expanded_security_token_location = os.path.expanduser(security_token_file)
    if not os.path.exists(expanded_security_token_location):
        click.echo("ERROR: Cannot refresh token, 'security_token_file' does not exist: {}".format(expanded_security_token_location))
        sys.exit(1)

    with open(expanded_security_token_location, 'r') as security_token_file:
        token = security_token_file.read()

    auth = oci.auth.signers.SecurityTokenSigner(token, oci.signer.load_private_key_from_file(client_config.get('key_file'), client_config.get('pass_phrase')))

    refresh_url = "https://auth.{region}.oraclecloud.com/v1/authentication/refresh".format(region=client_config.get('region'))
    click.echo("Attempting to refresh token from {refresh_url}".format(refresh_url=refresh_url), file=sys.stderr)

    response = requests.post(
        refresh_url,
        headers={
            'content-type': 'application/json'
        },
        data=json.dumps({
            'currentToken': token
        }),
        auth=auth
    )

    if response.status_code == 200:
        refreshed_token = json.loads(response.content.decode('UTF-8'))['token']

        with open(expanded_security_token_location, 'w') as security_token_file:
            security_token_file.write(refreshed_token)

        click.echo("Successfully refreshed token", file=sys.stderr)
    elif response.status_code == 401:
        click.echo("Your session is no longer valid and cannot be refreshed. Please use 'oci session authenticate' to create a new session.", file=sys.stderr)
        sys.exit(1)
    else:
        click.echo("ERROR: Failed to refresh sesison. Error: {}".format(str(response.content.decode('UTF-8'))))
        sys.exit(1)
    def validate_result(self, service_name, api_name, container_id, request,
                        result, data_field_name, is_delete_operation):
        """
        Calls the testing service to validate a CLI command result.

        :param str service_name
            The name of the service to request input parameters for. The testing service uses the following template to
            construct the Java SDK request object:
            String.format("com.oracle.bmc.%s.requests.%sRequest", serviceName, apiName);

        :param str api_name
            The name of the API to request input parameters for. The testing service uses the following template to
            construct the Java SDK request object:
            String.format("com.oracle.bmc.%s.requests.%sRequest", serviceName, apiName);

        :param str container_id:
            The ID of the current container.

        :param str request:
            The json content of the request object.

        :param Result result:
            The result object from click.testing.CliRunner.

        :param str data_field_name:
            The CLI returns the main resource under the field name 'data' but we need to convert it to
            the name used in the Java SDK (e.g. for GetGroup data -> group)

        :return: None

        """
        # standardize service name to convention for Java SDK model namespaces (all lower case one word)
        java_package_name = service_name.replace('_', '').lower()

        request_class = 'com.oracle.bmc.{java_package_name}.requests.{api_name}Request'.format(
            java_package_name=java_package_name, api_name=api_name)
        response_class = 'com.oracle.bmc.{java_package_name}.responses.{api_name}Response'.format(
            java_package_name=java_package_name, api_name=api_name)

        success_url = '{service_root_url}/response'.format(
            service_root_url=SERVICE_ROOT_URL)
        error_url = '{service_root_url}/error'.format(
            service_root_url=SERVICE_ROOT_URL)
        data = {
            'containerId': container_id,
            'requestClass': request_class,
            'requestJson': json.dumps(request),
        }

        params = {"sessionId": self.session_id, "lang": SERVICE_LANGUAGE}

        if result.exit_code == 0:
            # remove known warnings from output that would break JSON parsing
            output = result.output.replace(LIST_NOT_ALL_ITEMS_RETURNED_WARNING,
                                           '')

            # list and delete CLI commands can return an empty string so specially handle those cases
            if len(output) == 0:
                if api_name.lower().startswith('list'):
                    normalized_response_json = {data_field_name: []}
                elif is_delete_operation:
                    normalized_response_json = {}
                else:
                    raise ValueError('CLI output was empty')
            else:
                try:
                    normalized_response_json = json.loads(output)
                except ValueError as e:
                    print(
                        'Failed to parse CLI output as valid JSON. Output: {}'.
                        format(output))
                    raise e

                # convert the CLI response (keys with '-') to camelcase so that testing service can validate
                complex_parameter_type = {
                    'module': service_name,
                    'class': data_field_name[0].upper() + data_field_name[1:]
                }

                # remove 'data' temporarily to camelize all other fields
                response_data = normalized_response_json.pop('data', None)

                # camelize the rest of the response fields (data is removed so there is no corresponding complex type definition for the top level object)
                normalized_response_json = make_dict_keys_camel_case(
                    normalized_response_json)

                # camelize response['data'] independently and specify the corresponding complex_parameter_type
                # this will correctly skip camelizing keys of dictionaries nested within the response such as 'metadata' or 'defined-tags'
                if response_data:
                    normalized_response_json[
                        data_field_name] = make_dict_keys_camel_case(
                            response_data,
                            complex_parameter_type=complex_parameter_type)

            # right now we only return the opc-request-id for errors but the validator looks for it
            # tempporarily bypassing this check by hardcoding one here
            normalized_response_json[
                'opcRequestId'] = use_or_generate_request_id(None)

            data['responseJson'] = json.dumps(normalized_response_json)
            data['responseClass'] = response_class

            response = requests.post(success_url,
                                     params=params,
                                     data=json.dumps(data))
            assert response.status_code == 200, response.content
            return response.content.decode("UTF-8")
        else:
            error = json.loads(result.output.replace('ServiceError:', ''))
            error['statusCode'] = error.pop('status')
            error['opcRequestId'] = error.pop('opc-request-id')

            data['errorJson'] = json.dumps(error)
            print('errorToValidate: {}'.format(json.dumps(data, indent=2)))

            response = requests.post(error_url,
                                     params=params,
                                     data=json.dumps(data))
            assert response.status_code == 200, response.content
            return response.content.decode("UTF-8")
예제 #5
0
    def validate_result(self,
                        service_name,
                        api_name,
                        container_id,
                        request,
                        result,
                        data_field_name,
                        is_delete_operation,
                        is_binary_operation=False,
                        filename=None,
                        is_list_operation=False):
        """
        Calls the testing service to validate a CLI command result.

        :param str service_name
            The name of the service to request input parameters for. The testing service uses the following template to
            construct the Java SDK request object:
            String.format("com.oracle.bmc.%s.requests.%sRequest", serviceName, apiName);

        :param str api_name
            The name of the API to request input parameters for. The testing service uses the following template to
            construct the Java SDK request object:
            String.format("com.oracle.bmc.%s.requests.%sRequest", serviceName, apiName);

        :param str container_id:
            The ID of the current container.

        :param str request:
            The json content of the request object.

        :param Result result:
            The result object from click.testing.CliRunner.

        :param str data_field_name:
            The CLI returns the main resource under the field name 'data' but we need to convert it to
            the name used in the Java SDK (e.g. for GetGroup data -> group)

        :param is_delete_operation:
            Field to indicate if the operation is a delete operation

        :param is_binary_operation:
            Field to indicate if the operation produces binary output (default: False)

        :param filename:
            Field for the file to read from, for binary data (default: None)

        :return: None

        """
        # standardize service name to convention for Java SDK model namespaces (all lower case one word)
        java_package_name = service_name.replace('_', '').lower()

        request_class = 'com.oracle.bmc.{java_package_name}.requests.{api_name}Request'.format(
            java_package_name=java_package_name, api_name=api_name)
        response_class = 'com.oracle.bmc.{java_package_name}.responses.{api_name}Response'.format(
            java_package_name=java_package_name, api_name=api_name)

        success_url = '{service_root_url}/response'.format(
            service_root_url=SERVICE_ROOT_URL)
        error_url = '{service_root_url}/error'.format(
            service_root_url=SERVICE_ROOT_URL)
        data = {
            'containerId': container_id,
            'requestClass': request_class,
            'requestJson': json.dumps(request),
        }

        params = {"sessionId": self.session_id, "lang": SERVICE_LANGUAGE}

        if is_list_operation:
            data['responseJson'] = []
            for result_item in result:
                # remove known warnings from output that would break JSON parsing
                output = result_item.output.replace(
                    LIST_NOT_ALL_ITEMS_RETURNED_WARNING, '')
                if len(output) == 0:
                    normalized_response_json = {data_field_name: []}
                else:
                    normalized_response_json = self.process_output(
                        output, service_name, data_field_name)
                    if "opcTotalItems" in normalized_response_json and int(
                            normalized_response_json["opcTotalItems"]) == 0:
                        normalized_response_json = {data_field_name: []}
                normalized_response_json[
                    'opcRequestId'] = use_or_generate_request_id(None)
                data['responseJson'].append(normalized_response_json)
            data['responseJson'] = json.dumps(data['responseJson'])
            data['listResponse'] = True
            data['responseClass'] = response_class

            response = requests.post(success_url,
                                     params=params,
                                     data=json.dumps(data))
            assert response.status_code == 200, response.content
            return response.content.decode("UTF-8")

        if result.exit_code == 0:
            # remove known warnings from output that would break JSON parsing
            output = result.output.replace(LIST_NOT_ALL_ITEMS_RETURNED_WARNING,
                                           '')

            # list and delete CLI commands can return an empty string so specially handle those cases
            if is_binary_operation:
                with open(filename, 'rb') as f:
                    content = f.read()
                normalized_response_json = {
                    "inputStream":
                    str(base64.b64encode(content).decode('utf-8')),
                    "contentLength": len(content)
                }
            elif len(output) == 0:
                if api_name.lower().startswith('list'):
                    normalized_response_json = {data_field_name: []}
                else:
                    normalized_response_json = {}
            else:
                normalized_response_json = self.process_output(
                    output, service_name, data_field_name)

            normalized_response_json[
                'opcRequestId'] = use_or_generate_request_id(None)
            # right now we only return the opc-request-id for errors but the validator looks for it
            # temporarily bypassing this check by hardcoding one here
            data['responseJson'] = json.dumps(normalized_response_json)
            data['responseClass'] = response_class

            response = requests.post(success_url,
                                     params=params,
                                     data=json.dumps(data))
            assert response.status_code == 200, response.content
            return response.content.decode("UTF-8")
        else:
            error = json.loads(result.output.replace('ServiceError:', ''))
            error['statusCode'] = error.pop('status')
            error['opcRequestId'] = error.pop('opc-request-id')

            data['errorJson'] = json.dumps(error)
            print('errorToValidate: {}'.format(json.dumps(data, indent=2)))

            response = requests.post(error_url,
                                     params=params,
                                     data=json.dumps(data))
            assert response.status_code == 200, response.content
            return response.content.decode("UTF-8")
예제 #6
0
    def _get_security_token_from_auth_service(self):
        request_payload = {
            'certificate':
            auth_utils.sanitize_certificate_string(
                self.leaf_certificate_retriever.get_certificate_raw()),
            'publicKey':
            auth_utils.sanitize_certificate_string(
                self.session_key_supplier.get_key_pair()
                ['public'].public_bytes(Encoding.PEM,
                                        PublicFormat.SubjectPublicKeyInfo))
        }

        if self.intermediate_certificate_retrievers:
            retrieved_certs = []
            for retriever in self.intermediate_certificate_retrievers:
                retrieved_certs.append(
                    auth_utils.sanitize_certificate_string(
                        retriever.get_certificate_raw()))

            request_payload['intermediateCertificates'] = retrieved_certs

        fingerprint = crypto.load_certificate(
            crypto.FILETYPE_PEM,
            self.leaf_certificate_retriever.get_certificate_raw()).digest(
                'sha1').decode('utf-8')
        signer = AuthTokenRequestSigner(self.tenancy_id, fingerprint,
                                        self.leaf_certificate_retriever)

        if self.cert_bundle_verify:
            response = requests.post(self.federation_endpoint,
                                     json=request_payload,
                                     auth=signer,
                                     verify=self.cert_bundle_verify)
        else:
            response = requests.post(self.federation_endpoint,
                                     json=request_payload,
                                     auth=signer)

        parsed_response = None
        try:
            parsed_response = response.json()
        except ValueError:
            error_text = 'Unable to parse response from auth service ({}): {}'.format(
                self.federation_endpoint, response.text)

            # If the response was a 2xx but unparseable, raise it straight away because it implies a potential service issue. If
            # we have a non-2xx but it is not parseable that is a more ambiguous scenario (e.g. could have been an issue with a
            # proxy or LB and those generally won't emit a JSON response) so throw it out a ServiceError so it can fall into
            # retry logic (depending on the error code)
            if response.ok:
                raise RuntimeError(error_text)
            else:
                raise oci.exceptions.ServiceError(response.status_code,
                                                  response.reason,
                                                  response.headers, error_text)

        if not response.ok:
            raise oci.exceptions.ServiceError(response.status_code,
                                              parsed_response.get('code'),
                                              response.headers,
                                              parsed_response.get('message'))
        else:
            if 'token' in parsed_response:
                self.security_token = SecurityTokenContainer(
                    self.session_key_supplier,
                    response.json()['token'])
            else:
                raise RuntimeError(
                    'Could not find token in response from auth service ({}): {}'
                    .format(self.federation_endpoint, parsed_response))