Skip to content

Commit

Permalink
tests(starter): add tests for oauth2 and identity provider
Browse files Browse the repository at this point in the history
related to #4453
  • Loading branch information
joaquinfelici committed Dec 12, 2024
1 parent 7fd1db1 commit 2d02d61
Show file tree
Hide file tree
Showing 8 changed files with 619 additions and 38 deletions.
12 changes: 11 additions & 1 deletion spring-boot-starter/starter-security/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

<dependency>
<artifactId>camunda-bpm-spring-boot-starter-test</artifactId>
<groupId>${project.groupId}</groupId>
Expand All @@ -59,6 +58,17 @@
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.camunda.commons</groupId>
<artifactId>camunda-commons-testing</artifactId>
<version>${version}</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package my.own.custom.spring.boot.project;
package org.camunda.bpm.spring.boot.starter.security;

import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class SampleApplication {
Expand All @@ -31,15 +33,15 @@ public static void main(String... args) {
SpringApplication.run(SampleApplication.class, args);
}

@Bean
public TestRestTemplate restTemplate(RestTemplateBuilder builder) {
builder.requestFactory(() -> {
var factory = new HttpComponentsClientHttpRequestFactory();
var httpClient = HttpClientBuilder.create().disableRedirectHandling().build();
factory.setHttpClient(httpClient);
return factory;
});
return new TestRestTemplate(builder);
}
@RestController
@RequestMapping("/camunda/api/engine/engine")
public static class TestController {

@GetMapping("/{username}/user")
public List<Map<String, String>> getUserInfo(@PathVariable("username") String username) {
Map<String, String> users = new HashMap<>();
users.put("name", username);
return List.of(users);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. Camunda licenses this file to you under the Apache License,
* Version 2.0; you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.camunda.bpm.spring.boot.starter.security.oauth2;

import static java.lang.String.format;
import static org.mockito.Mockito.when;

import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import org.camunda.bpm.spring.boot.starter.security.SampleApplication;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.BeansException;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SampleApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public abstract class AbstractSpringSecurityIT {

protected static final String EXPECTED_NAME_DEFAULT = "[{\"name\":\"default\"}]";
protected static final String PROVIDER = "mock-provider";
protected static final String AUTHORIZED_USER = "bob";

protected String baseUrl;

@LocalServerPort
protected int port;

@Before
public void setup() throws Exception {
baseUrl = format("http://localhost:%d", port);
}

protected static Object getBeanForClass(Class<?> type, WebApplicationContext context) {
try {
return context.getBean(type);
} catch (BeansException e) {
return null;
}
}

protected OAuth2AuthenticationToken createToken(String user) {
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("USER");
OAuth2User oAuth2User = new DefaultOAuth2User(authorities, Map.of("name", user), "name");
return new OAuth2AuthenticationToken(oAuth2User, authorities, AbstractSpringSecurityIT.PROVIDER);
}

protected void createAuthorizedClient(OAuth2AuthenticationToken authenticationToken,
ClientRegistrationRepository registrations,
OAuth2AuthorizedClientService authorizedClientService) {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "value", Instant.now(), Instant.now().plus(Duration.ofDays(1)));
ClientRegistration clientRegistration = registrations.findByRegistrationId(authenticationToken.getAuthorizedClientRegistrationId());
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, authenticationToken.getName(), accessToken);
when(authorizedClientService.loadAuthorizedClient(AbstractSpringSecurityIT.PROVIDER, AbstractSpringSecurityIT.AUTHORIZED_USER)).thenReturn(authorizedClient);
}

public static class ResultCaptor<T> implements Answer<T> {
public T result = null;

@Override
public T answer(InvocationOnMock invocationOnMock) throws Throwable {
result = (T) invocationOnMock.callRealMethod();
return result;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,49 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.camunda.bpm.spring.boot.starter.security;
package org.camunda.bpm.spring.boot.starter.security.oauth2;

import jakarta.annotation.PostConstruct;
import my.own.custom.spring.boot.project.SampleApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.context.WebApplicationContext;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SampleApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CamundaBpmSampleApplicationTest {

private String baseUrl;

@LocalServerPort
private int port;
public class CamundaBpmSampleApplicationIT extends AbstractSpringSecurityIT {

@Autowired
private TestRestTemplate testRestTemplate;

@PostConstruct
public void postConstruct() {
baseUrl = "http://localhost:" + port;
@Autowired
private WebApplicationContext webApplicationContext;

@Test
public void testSpringSecurityAutoConfigurationCorrectlySet() {
// given oauth2 client not configured
// when retrieving config beans then only SpringSecurityDisabledAutoConfiguration is present
assertThat(getBeanForClass(CamundaSpringSecurityOAuth2AutoConfiguration.class, webApplicationContext)).isNull();
assertThat(getBeanForClass(CamundaBpmSpringSecurityDisableAutoConfiguration.class, webApplicationContext)).isNotNull();
}

@Test
public void webappApiIsAvailableAndAuthorized() {
public void testWebappApiIsAvailableAndRequiresAuthorization() {
// given oauth2 client disabled
// when calling the webapp api
ResponseEntity<String> entity = testRestTemplate.getForEntity(baseUrl + "/camunda/api/engine/engine/default/user", String.class);
// then webapp api returns unauthorized
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}

@Test
public void restApiIsAvailable() {
public void testRestApiIsAvailable() {
// given oauth2 client disabled
// when calling the rest api
ResponseEntity<String> entity = testRestTemplate.getForEntity(baseUrl + "/engine-rest/engine/", String.class);
// then rest api is accessible
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).isEqualTo("[{\"name\":\"default\"}]");
assertThat(entity.getBody()).isEqualTo(EXPECTED_NAME_DEFAULT);
}
}
Loading

0 comments on commit 2d02d61

Please sign in to comment.