MethodDispatch { public Object invoke(Object target, List args, ExecutionContext executionContext) throws NameNotFoundException, QueryInvocationTargetException { Object[] argsArray = args.toArray(); try { MethodInvocationAuthorizer authorizer = executionContext.getMethodInvocationAuthorizer(); if (executionContext.isCqQueryContext()) { if (!authorizer.authorize(_method, target)) { throw new NotAuthorizedException(UNAUTHORIZED_STRING + _method.getName()); } } else { boolean authorizationResult; Boolean cachedResult = (Boolean) executionContext.cacheGet(_method); if (cachedResult != null) { authorizationResult = cachedResult; } else { authorizationResult = authorizer.authorize(_method, target); executionContext.cachePut(_method, authorizationResult); } if (!authorizationResult) { throw new NotAuthorizedException(UNAUTHORIZED_STRING + _method.getName()); } } return _method.invoke(target, argsArray); } catch (IllegalAccessException e) { throw new NameNotFoundException( String.format("Method ' %s ' in class ' %s ' is not accessible to the query processor", _method.getName(), target.getClass().getName()), e); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof Exception) throw new QueryInvocationTargetException(t); throw new QueryInvocationTargetException(e); } } MethodDispatch(Class targetClass, String methodName, List argTypes); Object invoke(Object target, List args, ExecutionContext executionContext);  }
@Test public void invokeShouldReturnCorrectlyWhenMethodIsAuthorized() throws NameResolutionException, QueryInvocationTargetException { doReturn(true).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch methodDispatch = new MethodDispatch(TestBean.class, PUBLIC_METHOD_NAME, Collections.emptyList()); Object result = methodDispatch.invoke(testBean, emptyList, queryExecutionContext); assertThat(result).isInstanceOf(String.class); assertThat(result).isEqualTo(PUBLIC_METHOD_RETURN_VALUE); }
@Test public void invokeShouldThrowExceptionWhenMethodIsNotAuthorized() throws NameResolutionException { doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch methodDispatch = new MethodDispatch(TestBean.class, PUBLIC_METHOD_NAME, Collections.emptyList()); assertThatThrownBy(() -> methodDispatch.invoke(testBean, emptyList, queryExecutionContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + PUBLIC_METHOD_NAME); }
@Test public void invokeShouldCacheResultForAllowedMethod() throws Exception { doReturn(true).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch methodDispatch = new MethodDispatch(Integer.class, "toString", Collections.emptyList()); Method toStringMethod = Integer.class.getMethod("toString"); methodDispatch.invoke(new Integer("0"), emptyList, queryExecutionContext); assertThat(queryExecutionContext.cacheGet(toStringMethod)).isEqualTo(true); }
@Test public void invokeShouldNotCacheResultForAllowedMethodOnCqs() throws Exception { when(queryExecutionContext.isCqQueryContext()).thenReturn(true); doReturn(true).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch methodDispatch = new MethodDispatch(Integer.class, "toString", Collections.emptyList()); Method toStringMethod = Integer.class.getMethod("toString"); methodDispatch.invoke(new Integer("0"), emptyList, queryExecutionContext); assertThat(queryExecutionContext.cacheGet(toStringMethod)).isNull(); }
@Test public void invokeShouldCacheResultForForbiddenMethod() throws Exception { doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch methodDispatch = new MethodDispatch(Integer.class, "toString", Collections.emptyList()); assertThatThrownBy( () -> methodDispatch.invoke(new Integer("0"), emptyList, queryExecutionContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + "toString"); Method toStringMethod = Integer.class.getMethod("toString"); assertThat(queryExecutionContext.cacheGet(toStringMethod)).isEqualTo(false); }
@Test public void invokeShouldNotCacheResultForForbiddenMethodOnCqs() throws Exception { when(queryExecutionContext.isCqQueryContext()).thenReturn(true); doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch methodDispatch = new MethodDispatch(Integer.class, "toString", Collections.emptyList()); assertThatThrownBy( () -> methodDispatch.invoke(new Integer("0"), emptyList, queryExecutionContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + "toString"); Method toStringMethod = Integer.class.getMethod("toString"); assertThat(queryExecutionContext.cacheGet(toStringMethod)).isNull(); }
@Test public void invokeShouldUseCachedAuthorizerResultWhenMethodIsAuthorizedAndQueryContextIsTheSame() throws NameResolutionException { doReturn(true).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch publicMethodDispatch = new MethodDispatch(TestBean.class, PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch anotherPublicMethodDispatch = new MethodDispatch(TestBean.class, ANOTHER_PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch extendedPublicMethodDispatch = new MethodDispatch(TestBeanExtension.class, EXTENDED_PUBLIC_METHOD_NAME, Collections.emptyList()); IntStream.range(0, 10).forEach(element -> { try { assertThat(publicMethodDispatch.invoke(testBean, emptyList, queryExecutionContext)) .isInstanceOf(String.class).isEqualTo(PUBLIC_METHOD_RETURN_VALUE); assertThat(anotherPublicMethodDispatch.invoke(testBean, emptyList, queryExecutionContext)) .isInstanceOf(String.class).isEqualTo(ANOTHER_PUBLIC_METHOD_RETURN_VALUE); assertThat(extendedPublicMethodDispatch .invoke(new TestBeanExtension(), emptyList, queryExecutionContext)) .isInstanceOf(String.class).isEqualTo(EXTENDED_PUBLIC_METHOD_RETURN_VALUE); } catch (NameNotFoundException | QueryInvocationTargetException exception) { throw new RuntimeException(exception); } }); verify(methodInvocationAuthorizer, times(3)).authorize(any(), any()); }
@Test public void invokeShouldNotUseCachedAuthorizerResultWhenMethodIsAuthorizedAndQueryContextIsNotTheSame() throws NameResolutionException { doReturn(true).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch publicMethodDispatch = new MethodDispatch(TestBean.class, PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch anotherPublicMethodDispatch = new MethodDispatch(TestBean.class, ANOTHER_PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch extendedPublicMethodDispatch = new MethodDispatch(TestBeanExtension.class, EXTENDED_PUBLIC_METHOD_NAME, Collections.emptyList()); IntStream.range(0, 10).forEach(element -> { try { QueryExecutionContext mockContext = mock(QueryExecutionContext.class); when(mockContext.getMethodInvocationAuthorizer()).thenReturn(methodInvocationAuthorizer); assertThat(publicMethodDispatch.invoke(testBean, emptyList, mockContext)) .isInstanceOf(String.class).isEqualTo(PUBLIC_METHOD_RETURN_VALUE); assertThat(anotherPublicMethodDispatch.invoke(testBean, emptyList, mockContext)) .isInstanceOf(String.class).isEqualTo(ANOTHER_PUBLIC_METHOD_RETURN_VALUE); assertThat( extendedPublicMethodDispatch.invoke(new TestBeanExtension(), emptyList, mockContext)) .isInstanceOf(String.class).isEqualTo(EXTENDED_PUBLIC_METHOD_RETURN_VALUE); } catch (NameNotFoundException | QueryInvocationTargetException exception) { throw new RuntimeException(exception); } }); verify(methodInvocationAuthorizer, times(30)).authorize(any(), any()); }
@Test public void invokeShouldNotUseCachedAuthorizerResultWhenMethodIsAuthorizedAndQueryContextIsTheSameForCqs() throws NameResolutionException { when(queryExecutionContext.isCqQueryContext()).thenReturn(true); doReturn(true).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch publicMethodDispatch = new MethodDispatch(TestBean.class, PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch anotherPublicMethodDispatch = new MethodDispatch(TestBean.class, ANOTHER_PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch extendedPublicMethodDispatch = new MethodDispatch(TestBeanExtension.class, EXTENDED_PUBLIC_METHOD_NAME, Collections.emptyList()); IntStream.range(0, 10).forEach(element -> { try { assertThat(publicMethodDispatch.invoke(testBean, emptyList, queryExecutionContext)) .isInstanceOf(String.class).isEqualTo(PUBLIC_METHOD_RETURN_VALUE); assertThat(anotherPublicMethodDispatch.invoke(testBean, emptyList, queryExecutionContext)) .isInstanceOf(String.class).isEqualTo(ANOTHER_PUBLIC_METHOD_RETURN_VALUE); assertThat(extendedPublicMethodDispatch .invoke(new TestBeanExtension(), emptyList, queryExecutionContext)) .isInstanceOf(String.class).isEqualTo(EXTENDED_PUBLIC_METHOD_RETURN_VALUE); } catch (NameNotFoundException | QueryInvocationTargetException exception) { throw new RuntimeException(exception); } }); verify(methodInvocationAuthorizer, times(30)).authorize(any(), any()); }
@Test public void invokeShouldUseCachedAuthorizerResultWhenMethodIsNotAuthorizedAndContextIsTheSame() throws NameResolutionException { doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch publicMethodDispatch = new MethodDispatch(TestBean.class, PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch anotherPublicMethodDispatch = new MethodDispatch(TestBean.class, ANOTHER_PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch extendedPublicMethodDispatch = new MethodDispatch(TestBeanExtension.class, EXTENDED_PUBLIC_METHOD_NAME, Collections.emptyList()); IntStream.range(0, 10).forEach(element -> { assertThatThrownBy( () -> publicMethodDispatch.invoke(testBean, emptyList, queryExecutionContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + PUBLIC_METHOD_NAME); assertThatThrownBy( () -> anotherPublicMethodDispatch.invoke(testBean, emptyList, queryExecutionContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage( RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + ANOTHER_PUBLIC_METHOD_NAME); assertThatThrownBy(() -> extendedPublicMethodDispatch .invoke(new TestBeanExtension(), emptyList, queryExecutionContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage( RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + EXTENDED_PUBLIC_METHOD_NAME); }); verify(methodInvocationAuthorizer, times(3)).authorize(any(), any()); }
@Test public void invokeShouldNotUseCachedAuthorizerResultWhenMethodIsNotAuthorizedAndContextIsNotTheSame() throws NameResolutionException { doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch publicMethodDispatch = new MethodDispatch(TestBean.class, PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch anotherPublicMethodDispatch = new MethodDispatch(TestBean.class, ANOTHER_PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch extendedPublicMethodDispatch = new MethodDispatch(TestBeanExtension.class, EXTENDED_PUBLIC_METHOD_NAME, Collections.emptyList()); IntStream.range(0, 10).forEach(element -> { QueryExecutionContext mockContext = mock(QueryExecutionContext.class); when(mockContext.getMethodInvocationAuthorizer()).thenReturn(methodInvocationAuthorizer); assertThatThrownBy(() -> publicMethodDispatch.invoke(testBean, emptyList, mockContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + PUBLIC_METHOD_NAME); assertThatThrownBy(() -> anotherPublicMethodDispatch.invoke(testBean, emptyList, mockContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + ANOTHER_PUBLIC_METHOD_NAME); assertThatThrownBy(() -> extendedPublicMethodDispatch .invoke(new TestBeanExtension(), emptyList, mockContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage( RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + EXTENDED_PUBLIC_METHOD_NAME); }); verify(methodInvocationAuthorizer, times(30)).authorize(any(), any()); }
@Test public void invokeShouldNotUseCachedAuthorizerResultWhenMethodIsNotAuthorizedAndContextIsTheSameForCqs() throws NameResolutionException { when(queryExecutionContext.isCqQueryContext()).thenReturn(true); doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any()); MethodDispatch publicMethodDispatch = new MethodDispatch(TestBean.class, PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch anotherPublicMethodDispatch = new MethodDispatch(TestBean.class, ANOTHER_PUBLIC_METHOD_NAME, Collections.emptyList()); MethodDispatch extendedPublicMethodDispatch = new MethodDispatch(TestBeanExtension.class, EXTENDED_PUBLIC_METHOD_NAME, Collections.emptyList()); IntStream.range(0, 10).forEach(element -> { assertThatThrownBy( () -> publicMethodDispatch.invoke(testBean, emptyList, queryExecutionContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + PUBLIC_METHOD_NAME); assertThatThrownBy( () -> anotherPublicMethodDispatch.invoke(testBean, emptyList, queryExecutionContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage( RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + ANOTHER_PUBLIC_METHOD_NAME); assertThatThrownBy(() -> extendedPublicMethodDispatch .invoke(new TestBeanExtension(), emptyList, queryExecutionContext)) .isInstanceOf(NotAuthorizedException.class) .hasMessage( RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + EXTENDED_PUBLIC_METHOD_NAME); }); verify(methodInvocationAuthorizer, times(30)).authorize(any(), any()); }