Exemplo n.º 1
0
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DefaultIntegrationTestConfig.class)
@OAuth2ContextConfiguration(OAuth2ContextConfiguration.ClientCredentials.class)
public class AppApprovalIT {

  @Rule public ServerRunning serverRunning = ServerRunning.isRunning();

  UaaTestAccounts testAccounts = UaaTestAccounts.standard(serverRunning);

  @Rule
  public OAuth2ContextSetup context =
      OAuth2ContextSetup.withTestAccounts(serverRunning, testAccounts);

  @Rule
  public TestAccountSetup testAccountSetup = TestAccountSetup.standard(serverRunning, testAccounts);

  public RestOperations restTemplate;

  @Autowired @Rule public IntegrationTestRule integrationTestRule;

  @Autowired WebDriver webDriver;

  @Value("${integration.test.base_url}")
  String baseUrl;

  @Value("${integration.test.app_url}")
  String appUrl;

  @Before
  @After
  public void logout_and_clear_cookies() {
    restTemplate = serverRunning.getRestTemplate();

    try {
      webDriver.get(baseUrl + "/logout.do");
    } catch (org.openqa.selenium.TimeoutException x) {
      // try again - this should not be happening - 20 second timeouts
      webDriver.get(baseUrl + "/logout.do");
    }
    webDriver.get(appUrl + "/j_spring_security_logout");
    webDriver.manage().deleteAllCookies();
  }

  @Test
  public void testApprovingAnApp() throws Exception {
    ResponseEntity<SearchResults<ScimGroup>> getGroups =
        restTemplate.exchange(
            baseUrl + "/Groups?filter=displayName eq '{displayName}'",
            HttpMethod.GET,
            null,
            new ParameterizedTypeReference<SearchResults<ScimGroup>>() {},
            "cloud_controller.read");
    ScimGroup group = getGroups.getBody().getResources().stream().findFirst().get();

    group.setDescription("Read about your clouds.");
    HttpHeaders headers = new HttpHeaders();
    headers.add("If-Match", Integer.toString(group.getVersion()));
    HttpEntity request = new HttpEntity(group, headers);
    restTemplate.exchange(
        baseUrl + "/Groups/{group-id}", HttpMethod.PUT, request, Object.class, group.getId());

    ScimUser user = createUnapprovedUser();

    // Visit app
    webDriver.get(appUrl);

    // Sign in to login server
    webDriver.findElement(By.name("username")).sendKeys(user.getUserName());
    webDriver.findElement(By.name("password")).sendKeys(user.getPassword());
    webDriver.findElement(By.xpath("//input[@value='Sign in']")).click();

    // Authorize the app for some scopes
    Assert.assertEquals(
        "Application Authorization", webDriver.findElement(By.cssSelector("h1")).getText());

    webDriver
        .findElement(By.xpath("//label[text()='Change your password']/preceding-sibling::input"))
        .click();
    webDriver
        .findElement(
            By.xpath(
                "//label[text()='Read user IDs and retrieve users by ID']/preceding-sibling::input"))
        .click();
    webDriver.findElement(
        By.xpath("//label[text()='Read about your clouds.']/preceding-sibling::input"));

    webDriver.findElement(By.xpath("//button[text()='Authorize']")).click();

    Assert.assertEquals("Sample Home Page", webDriver.findElement(By.cssSelector("h1")).getText());

    // View profile on the login server
    webDriver.get(baseUrl + "/profile");

    Assert.assertFalse(
        webDriver.findElement(By.xpath("//input[@value='app-password.write']")).isSelected());
    Assert.assertFalse(
        webDriver.findElement(By.xpath("//input[@value='app-scim.userids']")).isSelected());
    Assert.assertTrue(
        webDriver
            .findElement(By.xpath("//input[@value='app-cloud_controller.read']"))
            .isSelected());
    Assert.assertTrue(
        webDriver
            .findElement(By.xpath("//input[@value='app-cloud_controller.write']"))
            .isSelected());

    // Add approvals
    webDriver.findElement(By.xpath("//input[@value='app-password.write']")).click();
    webDriver.findElement(By.xpath("//input[@value='app-scim.userids']")).click();

    webDriver.findElement(By.xpath("//button[text()='Update']")).click();

    Assert.assertTrue(
        webDriver.findElement(By.xpath("//input[@value='app-password.write']")).isSelected());
    Assert.assertTrue(
        webDriver.findElement(By.xpath("//input[@value='app-scim.userids']")).isSelected());
    Assert.assertTrue(
        webDriver
            .findElement(By.xpath("//input[@value='app-cloud_controller.read']"))
            .isSelected());
    Assert.assertTrue(
        webDriver
            .findElement(By.xpath("//input[@value='app-cloud_controller.write']"))
            .isSelected());

    // Revoke app
    webDriver.findElement(By.linkText("Revoke Access")).click();

    Assert.assertEquals(
        "Are you sure you want to revoke access to The Ultimate Oauth App?",
        webDriver.findElement(By.cssSelector(".revocation-modal p")).getText());

    // click cancel
    webDriver.findElement(By.cssSelector("#app-form .revocation-cancel")).click();

    webDriver.findElement(By.linkText("Revoke Access")).click();

    // click confirm
    webDriver.findElement(By.cssSelector("#app-form .revocation-confirm")).click();

    Assert.assertThat(
        webDriver.findElements(By.xpath("//input[@value='app-password.write']")), Matchers.empty());
  }

  @Test
  public void testInvalidAppRedirectDisplaysError() throws Exception {
    ScimUser user = createUnapprovedUser();

    // given we vist the app (specifying an invalid redirect - incorrect protocol https)
    webDriver.get(appUrl + "?redirect_uri=https://localhost:8080/app/");

    // Sign in to login server
    webDriver.findElement(By.name("username")).sendKeys(user.getUserName());
    webDriver.findElement(By.name("password")).sendKeys(user.getPassword());
    webDriver.findElement(By.xpath("//input[@value='Sign in']")).click();

    // Authorize the app for some scopes
    assertThat(
        webDriver.findElement(By.className("alert-error")).getText(),
        RegexMatcher.matchesRegex(
            "^Invalid redirect (.*) did not match one of the registered values"));
  }

  private ScimUser createUnapprovedUser() throws Exception {
    String userName = "******" + new RandomValueStringGenerator().generate();
    String userEmail = userName + "@example.com";

    RestOperations restTemplate = serverRunning.getRestTemplate();

    ScimUser user = new ScimUser();
    user.setUserName(userName);
    user.setPassword("s3Cretsecret");
    user.addEmail(userEmail);
    user.setActive(true);
    user.setVerified(true);

    ResponseEntity<ScimUser> result =
        restTemplate.postForEntity(serverRunning.getUrl("/Users"), user, ScimUser.class);
    assertEquals(HttpStatus.CREATED, result.getStatusCode());

    return user;
  }

  public static class RegexMatcher extends TypeSafeMatcher<String> {

    private final String regex;

    public RegexMatcher(final String regex) {
      this.regex = regex;
    }

    @Override
    public void describeTo(final Description description) {
      description.appendText("matches regex=`" + regex + "`");
    }

    @Override
    public boolean matchesSafely(final String string) {
      return string.matches(regex);
    }

    public static RegexMatcher matchesRegex(final String regex) {
      return new RegexMatcher(regex);
    }
  }
}
/** @author Dave Syer */
public class PasswordChangeEndpointIntegrationTests {

  private final String JOE = "joe_" + new RandomValueStringGenerator().generate().toLowerCase();

  private final String userEndpoint = "/Users";

  @Rule public ServerRunning serverRunning = ServerRunning.isRunning();

  private UaaTestAccounts testAccounts = UaaTestAccounts.standard(serverRunning);

  @Rule
  public TestAccountSetup testAccountSetup = TestAccountSetup.standard(serverRunning, testAccounts);

  @Rule
  public OAuth2ContextSetup context =
      OAuth2ContextSetup.withTestAccounts(serverRunning, testAccounts);

  private RestOperations client;

  private ScimUser joe;

  private ResponseEntity<ScimUser> createUser(
      String username, String firstName, String lastName, String email) {
    ScimUser user = new ScimUser();
    user.setUserName(username);
    user.setName(new ScimUser.Name(firstName, lastName));
    user.addEmail(email);
    user.setPassword("pas5Word");
    user.setVerified(true);
    return client.postForEntity(serverRunning.getUrl(userEndpoint), user, ScimUser.class);
  }

  @Before
  public void createRestTemplate() throws Exception {
    Assume.assumeTrue(!testAccounts.isProfileActive("vcap"));
    client = serverRunning.getRestTemplate();
    ((RestTemplate) serverRunning.getRestTemplate())
        .setErrorHandler(
            new OAuth2ErrorHandler(context.getResource()) {
              // Pass errors through in response entity for status code analysis
              @Override
              public boolean hasError(ClientHttpResponse response) throws IOException {
                return false;
              }

              @Override
              public void handleError(ClientHttpResponse response) throws IOException {}
            });
  }

  @BeforeOAuth2Context
  @OAuth2ContextConfiguration(OAuth2ContextConfiguration.ClientCredentials.class)
  public void createAccount() throws Exception {
    client = serverRunning.getRestTemplate();
    ResponseEntity<ScimUser> response = createUser(JOE, "Joe", "User", "*****@*****.**");
    joe = response.getBody();
    assertEquals(JOE, joe.getUserName());
  }

  // curl -v -H "Content-Type: application/json" -X PUT -H
  // "Accept: application/json" --data
  // "{\"password\":\"newpassword\",\"schemas\":[\"urn:scim:schemas:core:1.0\"]}"
  // http://localhost:8080/uaa/User/{id}/password
  @Test
  @OAuth2ContextConfiguration(OAuth2ContextConfiguration.ClientCredentials.class)
  public void testChangePasswordSucceeds() throws Exception {
    PasswordChangeRequest change = new PasswordChangeRequest();
    change.setPassword("Newpasswo3d");

    HttpHeaders headers = new HttpHeaders();
    ResponseEntity<Void> result =
        client.exchange(
            serverRunning.getUrl(userEndpoint) + "/{id}/password",
            HttpMethod.PUT,
            new HttpEntity<>(change, headers),
            Void.class,
            joe.getId());
    assertEquals(HttpStatus.OK, result.getStatusCode());
  }

  @Test
  @OAuth2ContextConfiguration(OAuth2ContextConfiguration.ClientCredentials.class)
  public void testChangePasswordSameAsOldFails() {
    PasswordChangeRequest change = new PasswordChangeRequest();
    change.setPassword("pas5Word");

    HttpHeaders headers = new HttpHeaders();
    ResponseEntity<Void> result =
        client.exchange(
            serverRunning.getUrl(userEndpoint) + "/{id}/password",
            HttpMethod.PUT,
            new HttpEntity<>(change, headers),
            Void.class,
            joe.getId());
    assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, result.getStatusCode());
  }

  @Test
  @OAuth2ContextConfiguration(
      resource = OAuth2ContextConfiguration.Implicit.class,
      initialize = false)
  public void testUserChangesOwnPassword() throws Exception {

    MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
    parameters.set("source", "credentials");
    parameters.set("username", joe.getUserName());
    parameters.set("password", "pas5Word");
    context.getAccessTokenRequest().putAll(parameters);

    PasswordChangeRequest change = new PasswordChangeRequest();
    change.setOldPassword("pas5Word");
    change.setPassword("Newpasswo3d");

    HttpHeaders headers = new HttpHeaders();
    ResponseEntity<Void> result =
        client.exchange(
            serverRunning.getUrl(userEndpoint) + "/{id}/password",
            HttpMethod.PUT,
            new HttpEntity<>(change, headers),
            Void.class,
            joe.getId());
    assertEquals(HttpStatus.OK, result.getStatusCode());
  }

  @Test
  @OAuth2ContextConfiguration(
      resource = OAuth2ContextConfiguration.Implicit.class,
      initialize = false)
  public void testUserMustSupplyOldPassword() throws Exception {

    MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
    parameters.set("source", "credentials");
    parameters.set("username", joe.getUserName());
    parameters.set("password", "pas5Word");
    context.getAccessTokenRequest().putAll(parameters);

    PasswordChangeRequest change = new PasswordChangeRequest();
    change.setPassword("Newpasswo3d");

    HttpHeaders headers = new HttpHeaders();
    ResponseEntity<Void> result =
        client.exchange(
            serverRunning.getUrl(userEndpoint) + "/{id}/password",
            HttpMethod.PUT,
            new HttpEntity<>(change, headers),
            Void.class,
            joe.getId());
    assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode());
  }

  @Test
  @OAuth2ContextConfiguration(
      resource = OAuth2ContextConfiguration.ClientCredentials.class,
      initialize = false)
  public void testUserAccountGetsUnlockedAfterPasswordChange() throws Exception {

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
    headers.set("Authorization", testAccounts.getAuthorizationHeader("app", "appclientsecret"));

    MultiValueMap<String, String> data = new LinkedMultiValueMap<String, String>();
    data.put("grant_type", Collections.singletonList("password"));
    data.put("username", Collections.singletonList(joe.getUserName()));
    data.put("password", Collections.singletonList("pas5Word"));

    ResponseEntity<Map> result =
        serverRunning.postForMap(
            serverRunning.buildUri("/oauth/token").build().toString(), data, headers);
    assertEquals(HttpStatus.OK, result.getStatusCode());

    // Lock out the account
    data.put("password", Collections.singletonList("randomPassword1"));

    for (int i = 0; i < 5; i++) {
      result =
          serverRunning.postForMap(
              serverRunning.buildUri("/oauth/token").build().toString(), data, headers);
      assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode());
    }

    // Check that it is locked
    result =
        serverRunning.postForMap(
            serverRunning.buildUri("/oauth/token").build().toString(), data, headers);
    assertEquals("Login policy rejected authentication", result.getBody().get("error_description"));
    assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode());

    PasswordChangeRequest change = new PasswordChangeRequest();
    change.setPassword("Newpasswo3d");

    MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
    parameters.set("grant_type", "client_credentials");
    parameters.set("username", "admin");
    parameters.set("password", "adminsecret");
    context.getAccessTokenRequest().putAll(parameters);

    // Change the password
    HttpHeaders passwordChangeHeaders = new HttpHeaders();
    ResponseEntity<Void> passwordChangeResult =
        client.exchange(
            serverRunning.getUrl(userEndpoint) + "/{id}/password",
            HttpMethod.PUT,
            new HttpEntity<>(change, passwordChangeHeaders),
            Void.class,
            joe.getId());
    assertEquals(HttpStatus.OK, passwordChangeResult.getStatusCode());

    MultiValueMap<String, String> newData = new LinkedMultiValueMap<String, String>();
    newData.put("grant_type", Collections.singletonList("password"));
    newData.put("username", Collections.singletonList(joe.getUserName()));
    newData.put("password", Collections.singletonList("Newpasswo3d"));

    ResponseEntity<Map> updatedResult =
        serverRunning.postForMap(
            serverRunning.buildUri("/oauth/token").build().toString(), newData, headers);
    assertEquals(HttpStatus.OK, updatedResult.getStatusCode());
  }
}