def invoke(self, service_id, operation_id, input_value, ctx): """ Invoke an API request :type service_id: :class:`str` :param service_id: Service identifier :type operation_id: :class:`str` :param operation_id: Operation identifier :type input_value: :class:`vmware.vapi.data.value.StructValue` :param input_value: Method input parameters :type ctx: :class:`vmware.vapi.core.ExecutionContext` :param ctx: Execution context for this method :rtype: :class:`vmware.vapi.core.MethodResult` :return: Result of the method invocation """ sec_ctx = ctx.security_context app_ctx = ctx.application_context authn_result = None request_scheme = sec_ctx.get(SCHEME_ID) if (self._authn_handlers and request_scheme and request_scheme != NO_AUTH): for handler in self._authn_handlers: # Call authenticate method and get the UserIdentity try: authn_result = handler.authenticate(sec_ctx) except Exception as e: logger.exception( 'Error in invoking authentication handler %s - %s', handler, e) error_value = make_error_value_from_msg_id( self._internal_server_error_def, 'vapi.security.authentication.exception', str(e)) return MethodResult(error=error_value) # authn result false means authentication failed if authn_result is False: error_value = make_error_value_from_msg_id( self._unauthenticated_error_def, 'vapi.security.authentication.invalid') return MethodResult(error=error_value) if authn_result is not None: # Matching authN handler found break if authn_result: # Add the authN identity to the security context to pass on to next # provider sec_ctx[AUTHN_IDENTITY] = authn_result ctx = ExecutionContext(app_ctx, sec_ctx) else: # No AuthN handler found pass an empty security context ctx = ExecutionContext(app_ctx, SecurityContext()) return ApiProviderFilter.invoke(self, service_id, operation_id, input_value, ctx)
def test_authn_op_without_sec_ctx(self): ctx = ExecutionContext(None, None) input_val = VoidValue() method_result = self.sec_ctx_filter.invoke('svc', 'op2', input_val, ctx) self.assertEqual(method_result.output, None) self.assertEqual(method_result.error, error_values[3])
def test_op(self): ctx = ExecutionContext(ApplicationContext(), None) input_val = VoidValue() method_result = self.sec_ctx_filter.invoke('svc', 'op1', input_val, ctx) self.assertEqual(method_result.output, IntegerValue(10)) self.assertEqual(method_result.error, None)
def test_introspection_operation_get(self): ctx = ExecutionContext() input_val = StructValue(OPERATION_INPUT) input_val.set_field('service_id', StringValue('mockup_interface')) input_val.set_field('operation_id', StringValue('mock')) actual_output = self.local_provider.invoke( 'com.vmware.vapi.std.introspection.operation', 'get', input_val, ctx) expected_output = StructValue( name='com.vmware.vapi.std.introspection.operation.info') input_def_value = StructValue( 'com.vmware.vapi.std.introspection.operation.data_definition') input_def_value.set_field('fields', OptionalValue(ListValue(values=[]))) input_def_value.set_field('element_definition', OptionalValue()) input_def_value.set_field('type', StringValue('STRUCTURE')) input_def_value.set_field('name', OptionalValue(StringValue(OPERATION_INPUT))) expected_output.set_field('input_definition', input_def_value) output_def_value = StructValue( 'com.vmware.vapi.std.introspection.operation.data_definition') output_def_value.set_field('fields', OptionalValue()) output_def_value.set_field('element_definition', OptionalValue()) output_def_value.set_field('type', StringValue('VOID')) output_def_value.set_field('name', OptionalValue()) expected_output.set_field('output_definition', output_def_value) expected_output.set_field('error_definitions', error_values) self.assertEqual(actual_output.output, expected_output)
def invoke(self, service_id, operation_id, input_value, ctx): """ Invoke an API request :type service_id: :class:`str` :param service_id: Service identifier :type operation_id: :class:`str` :param operation_id: Operation identifier :type input_value: :class:`vmware.vapi.data.value.StructValue` :param input_value: Method input parameters :type ctx: :class:`vmware.vapi.core.ExecutionContext` :param ctx: Execution context for this method :rtype: :class:`vmware.vapi.core.MethodResult` :return: Result of the method invocation """ app_ctx = ctx.application_context if ctx else None on_error = False for i in range(0, self.get_max_retries() + 1): # Typically get_security_context() should always return a valid # security context. In case of LegacySecurityContextFilter, # get_security_context() may return None if no security context is # set and hence needs to be handled. sec_ctx = (self.get_security_context(on_error) or (ctx.security_context if ctx else None)) new_ctx = ExecutionContext(app_ctx, sec_ctx) method_result = ApiProviderFilter.invoke( self, service_id, operation_id, input_value, new_ctx) if (method_result.error is None or not self.should_retry(method_result.error)): return method_result on_error = True
def test_noauth_op_unknown_op(self): sec_ctx = None app_ctx = ApplicationContext() ctx = ExecutionContext(app_ctx, sec_ctx) input_val = VoidValue() method_result = self.authz_filter.invoke('com', 'op', input_val, ctx) self.assertEqual(method_result.output, IntegerValue(10)) self.assertEqual(method_result.error, None)
def test_invoke_error(self): ctx = ExecutionContext() input_ = StructValue(method_name) input_.set_field('message', IntegerValue(10)) input_.set_field('throw', BooleanValue(True)) actual_method_result = self.provider.invoke(interface_name, method_name, input_, ctx) self.assertFalse(actual_method_result.success())
def test_success_2(self): ctx = ExecutionContext() service_id = interface_id.get_name() operation_id = mock_definition input_value = StructValue(OPERATION_INPUT) input_value.set_field(param_name, IntegerValue(10)) method_result = self.local_provider.invoke(service_id, operation_id, input_value, ctx) self.assertTrue(method_result.success()) self.assertEqual(method_result.output, VoidValue())
def _test_operation_not_found(self, interface_name, method_name, error, message_id, *args): ctx = ExecutionContext() expected_msg = message_factory.get_message(message_id, *args) expected_error_value = make_error_value_from_msgs(error, expected_msg) method_result = self.local_provider.invoke(interface_name, method_name, StructValue(), ctx) self.assertEquals(None, method_result.output) self.assertEquals(expected_error_value, method_result.error)
def test_success(self): ctx = ExecutionContext() service_id = interface_id.get_name() operation_id = mock_definition method_def = self.introspection.get_method(service_id, operation_id) input_value = method_def.get_input_definition().new_value() input_value.set_field(param_name, IntegerValue(10)) method_result = self.local_provider.invoke(service_id, operation_id, input_value, ctx) self.assertTrue(method_result.success()) self.assertEqual(method_result.output, VoidValue())
def test_invalid_user_pwd_scheme(self): sec_ctx = SecurityContext({ SCHEME_ID: OAUTH_SCHEME_ID, ACCESS_TOKEN: 'token' }) app_ctx = ApplicationContext() ctx = ExecutionContext(app_ctx, sec_ctx) input_val = VoidValue() method_result = self.authn_filter.invoke('com.pkg.svc', 'op', input_val, ctx) self.assertEqual(method_result.output, IntegerValue(10)) self.assertEqual(method_result.error, None)
def test_authn_op_with_sec_ctx(self): sec_ctx = SecurityContext({ SCHEME_ID: USER_PASSWORD_SCHEME_ID, USER_KEY: 'testuser', PASSWORD_KEY: 'password' }) ctx = ExecutionContext(None, sec_ctx) input_val = VoidValue() method_result = self.sec_ctx_filter.invoke('svc', 'op2', input_val, ctx) self.assertEqual(method_result.output, IntegerValue(20)) self.assertEqual(method_result.error, None)
def get_service_info(self, service_id): provider = self._get_provider(service_id) if provider: ctx = ExecutionContext() struct_value = StructValue(name=OPERATION_INPUT, values={'id': StringValue(service_id)}) return provider.invoke(Introspection.SERVICE_SVC, 'get', struct_value, ctx) else: msg = message_factory.get_message( 'vapi.introspection.operation.service.not_found', service_id) error_value = make_error_value_from_msgs(not_found_def, msg) return MethodResult(error=error_value)
def test_session_security_context(self): input_val = StructValue(name='operation-input', values={}) session_id = 'some-session-id' sec_ctx = create_session_security_context(session_id) _, actual_headers, actual_body, _ = RestSerializer.serialize_request( input_value=input_val, ctx=ExecutionContext(security_context=sec_ctx), rest_metadata=None, is_vapi_rest=True) expected_body = '{}' expected_headers = {REST_SESSION_ID_KEY: session_id} self.assertEqual(expected_headers, actual_headers) self.assertEqual(expected_body, actual_body)
def test_op_with_sec_ctx_on_filter(self): sec_ctx = SecurityContext({ SCHEME_ID: USER_PASSWORD_SCHEME_ID, USER_KEY: 'testuser', PASSWORD_KEY: 'password' }) self.sec_ctx_filter.set_security_context(sec_ctx) ctx = ExecutionContext(ApplicationContext(), None) input_val = VoidValue() method_result = self.sec_ctx_filter.invoke('svc', 'op1', input_val, ctx) self.assertEqual(method_result.output, IntegerValue(10)) self.assertEqual(method_result.error, None)
def new_context(self): """ create new execution context object :rtype: :class:`vmware.vapi.core.ExecutionContext` :return: execution context """ app_ctx = self._application_context # Create a default application context only if # the user has not provided anything if app_ctx is None: app_ctx = create_default_application_context() return ExecutionContext(app_ctx)
def test_user_pwd_scheme(self): sec_ctx = SecurityContext({ SCHEME_ID: USER_PASSWORD_SCHEME_ID, USER_KEY: 'testuser', PASSWORD_KEY: 'password' }) app_ctx = ApplicationContext() ctx = ExecutionContext(app_ctx, sec_ctx) input_val = VoidValue() method_result = self.authn_filter.invoke('com.pkg.svc', 'op', input_val, ctx) self.assertEqual(method_result.output, IntegerValue(10)) self.assertEqual(method_result.error, None)
def execution_context(ctx): """ get execution context from jsonrpc dict :type ctx: :class:`dict` :param ctx: json execution context :rtype: :class:`vmware.vapi.core.ExecutionContext` :return: execution context """ app_ctx = JsonRpcDictToVapi.app_ctx(ctx.get('appCtx')) security_ctx = JsonRpcDictToVapi.security_ctx(ctx.get('securityCtx')) execution_context = ExecutionContext(app_ctx, security_ctx) return execution_context
def test_invalid_user_pwd(self): sec_ctx = SecurityContext({ SCHEME_ID: USER_PASSWORD_SCHEME_ID, USER_KEY: 'testuser', PASSWORD_KEY: 'invalidpassword' }) app_ctx = ApplicationContext() ctx = ExecutionContext(app_ctx, sec_ctx) input_val = VoidValue() method_result = self.authn_filter.invoke('com.pkg.svc', 'op', input_val, ctx) self.assertEqual(method_result.error.name, 'com.vmware.vapi.std.errors.unauthenticated')
def test_noauth_op_with_valid_user(self): sec_ctx = SecurityContext({ SCHEME_ID: USER_PASSWORD_SCHEME_ID, USER_KEY: 'testuser', PASSWORD_KEY: 'password', AUTHN_IDENTITY: UserIdentity('testuser') }) app_ctx = ApplicationContext() ctx = ExecutionContext(app_ctx, sec_ctx) input_val = VoidValue() method_result = self.authz_filter.invoke('com.pkg.svc', 'op1', input_val, ctx) self.assertEqual(method_result.output, IntegerValue(10)) self.assertEqual(method_result.error, None)
def test_invoke_long_running_sync(self): ctx = ExecutionContext() input_ = StructValue(method_name) input_.set_field('message', StringValue('hello')) input_.set_field('throw', BooleanValue(False)) actual_method_result = self.provider.invoke(interface_name, task_method_name, input_, ctx) expected_method_result = MethodResult(output=StringValue('hello')) self.assertEqual(actual_method_result.output, expected_method_result.output) self.assertEqual(actual_method_result.error, expected_method_result.error)
def test_invalid_input(self): ctx = ExecutionContext() expected_msg = message_factory.get_message('vapi.method.input.invalid') expected_error_value = make_error_value_from_msgs( invalid_argument_def, expected_msg) method_result = self.local_provider.invoke( service_id=raise_python_exception_id.get_interface_identifier( ).get_name(), operation_id=raise_python_exception_id.get_name(), input_value=IntegerValue(), ctx=ctx) self.assertEquals(None, method_result.output) self.assertEquals(expected_error_value, method_result.error)
def setUpClass(cls): cls.task_manager = get_task_manager() msg_factory = MessageFactory(cls.msgs) cls.description = LocalizableMessage(id='msg1', default_message='task1', args=[]) cls.progress_msg = LocalizableMessage(id='prog1', default_message='progress', args=[]) cls.task_id = cls.task_manager.create_task(str(uuid.uuid4()), cls.description, 'test.service', 'op1', True) app_ctx = create_default_application_context() app_ctx[TASK_ID] = cls.task_id sec_ctx = SecurityContext() TLS.ctx = ExecutionContext(app_ctx, sec_ctx) cls.task_handle = get_task_handle(Info, StringType())
def get_checksum(self): # XXX: Resultant checksum does not include the data from the services # registered in the local provider of the aggregator. Since no one is # using Python aggregator, making this a low priority now. ctx = ExecutionContext() struct_value = StructValue(name=OPERATION_INPUT) providers = [ provider for provider in list(self._service_data.values()) if provider.__class__.__name__ != self._local_provider_class_name ] method_results = [ provider.invoke(Introspection.PROVIDER_SVC, 'get', struct_value, ctx) for provider in providers ] checksums = [ result.output.get_field('checksum') for result in method_results if result.success() ] return generate_fingerprint(str(checksums))
def test_username_security_context(self): input_val = StructValue(name='operation-input', values={}) user, password = '******', 'pwd' sec_ctx = create_user_password_security_context(user, password) _, actual_headers, actual_body, _ = RestSerializer.serialize_request( input_value=input_val, ctx=ExecutionContext(security_context=sec_ctx), rest_metadata=None, is_vapi_rest=True) expected_body = '{}' if six.PY2: b64_credentials = base64.b64encode('%s:%s' % (user, password)) authorization_val = 'Basic %s' % b64_credentials else: b64_credentials = base64.b64encode( bytes('%s:%s' % (user, password), 'utf-8')) authorization_val = b'Basic ' + b64_credentials expected_headers = {'Authorization': authorization_val} self.assertEqual(expected_headers, actual_headers) self.assertEqual(expected_body, actual_body)
def test_invoke_long_running(self): ctx = ExecutionContext() input_ = StructValue(method_name) input_.set_field('message', StringValue('hello')) input_.set_field('throw', BooleanValue(False)) actual_method_result = self.provider.invoke(interface_name, '%s$task' % task_method_name, input_, ctx) expected_method_result = MethodResult(output=StringValue('hello')) try: id_split = actual_method_result.output.value.split(':') uuid.UUID(id_split[0], version=4) uuid.UUID(id_split[1], version=4) except ValueError: # There's no assertNotRaises so explicitly fail assert # in case a ValueError is thrown for invalid uuid. self.assertTrue(False) self.assertEqual(actual_method_result.error, expected_method_result.error)
def new_context(self, runtime_data=None): """ create new execution context object :type runtime_data: :class:`vmware.vapi.core.RuntimeData` # pylint: disable=line-too-long :param runtime_data: Http runtime data :rtype: :class:`vmware.vapi.core.ExecutionContext` :return: execution context """ app_ctx = self._application_context # Create a default application context only if # the user has not provided anything if app_ctx is None: app_ctx = create_default_application_context() if runtime_data is None: runtime_data = RuntimeData() return ExecutionContext(application_context=app_ctx, runtime_data=runtime_data)
def test_decimal_json_request(self): ctx = ExecutionContext() template_request = ( '{"params":{"input":%s,"serviceId":"mock","ctx":{"appCtx":{}},' + '"operationId":"mock"},"jsonrpc":' + '"2.0","method":"invoke","id":1}' ) template_response = ( '{"jsonrpc":"2.0","id":1,"result":{"output": %s}}' ) for val, canonical_val in [ ('-12.34e4', '-1.234E5'), ('1E-130', '1.0E-130'), ('0.0E-0', '0.0E0'), ('1.2', '1.2E0'), ('1111111.1111100021e-30', '1.1111111111100021E-24'), ('12312423.0', '1.2312423E7'), ('0.000234E-10', '2.34E-14'), ('17', '1.7E1')]: decimal_val = decimal.Decimal(val) canonical_decimal_val = canonicalize_double(decimal_val) data_value = DoubleValue(decimal_val) params = { 'ctx': ctx, 'serviceId': 'mock', 'operationId': 'mock', 'input': data_value } actual_request = vapi_jsonrpc_request_factory( method=VAPI_INVOKE, params=params, id=1).serialize() expected_request = template_request % canonical_decimal_val self.assertEqual( json.loads(actual_request, parse_float=decimal.Decimal), json.loads(expected_request, parse_float=decimal.Decimal)) response_msg = template_response % canonical_val response = deserialize_response(response_msg) method_result = JsonRpcDictToVapi.method_result(response.result) self.assertTrue(method_result.success()) self.assertTrue(isinstance(method_result.output, DoubleValue)) self.assertEqual(method_result.output, DoubleValue(decimal.Decimal(canonical_val)))
def test_input_validation_failure(self): ctx = ExecutionContext() expected_msg0 = message_factory.get_message( 'vapi.data.structure.field.invalid', 'param', OPERATION_INPUT) expected_msg1 = message_factory.get_message( 'vapi.data.validate.mismatch', 'Integer', 'Structure') expected_error_value = make_error_value_from_msgs( invalid_argument_def, expected_msg0, expected_msg1) service_id = raise_python_exception_id.get_interface_identifier( ).get_name() operation_id = raise_python_exception_id.get_name() method_def = self.introspection.get_method(service_id, operation_id) input_value = method_def.get_input_definition().new_value() input_value.set_field(param_name, StructValue()) method_result = self.local_provider.invoke(service_id=service_id, operation_id=operation_id, input_value=input_value, ctx=ctx) self.assertEquals(None, method_result.output) self.assertEquals(expected_error_value, method_result.error)
def test_json_loopback_positive(self): connector = self.get_loopback_connector(self.provider) remote = connector.get_api_provider() for method_def in [null_method_def, echo_method_def]: # invoke_method ctx = ExecutionContext() params = build_adhoc_data_value(method_def.get_input_definition()) method_id = echo_method_def.get_identifier().get_name() remote_result = remote.invoke(fake_iface_id, method_id, params, ctx) local_result = self.provider.invoke(fake_iface_id, method_id, params, ctx) if remote_result.success(): # Validate result output method_def.get_output_definition().validate(remote_result.output) # Validate direct call self.assertEqual(remote_result.output, local_result.output) else: self.assertTrue(remote_result.error, local_result.error)