org.cloudfoundry.identity.uaa.scim.ScimUserEndpointsTests.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudfoundry.identity.uaa.scim.ScimUserEndpointsTests.java

Source

/*
 * Cloud Foundry 2012.02.03 Beta
 * Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
 *
 * This product is licensed to you under the Apache License, Version 2.0 (the "License").
 * You may not use this product except in compliance with the License.
 *
 * This product includes a number of subcomponents with
 * separate copyright notices and license terms. Your use of these
 * subcomponents is subject to the terms and conditions of the
 * subcomponent's license, as noted in the LICENSE file.
 */
package org.cloudfoundry.identity.uaa.scim;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.internal.matchers.StringContains.containsString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.error.ConvertingExceptionView;
import org.cloudfoundry.identity.uaa.error.ExceptionReportHttpMessageConverter;
import org.cloudfoundry.identity.uaa.scim.groups.*;
import org.cloudfoundry.identity.uaa.security.SecurityContextAccessor;
import org.cloudfoundry.identity.uaa.test.TestUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.servlet.View;

/**
 * @author Dave Syer
 * @author Luke Taylor
 *
 */
public class ScimUserEndpointsTests {

    @Rule
    public ExpectedException expected = ExpectedException.none();

    private static ScimUser joel;

    private static ScimUser dale;

    private static ScimUserEndpoints endpoints;

    private static ScimGroupEndpoints groupEndpoints;

    private static JdbcScimUserProvisioning dao;

    private static JdbcScimGroupMembershipManager mm;

    private static EmbeddedDatabase database;

    private List<ScimUser> createdUsers = new ArrayList<ScimUser>();

    private List<ScimGroup> createdGroups = new ArrayList<ScimGroup>();

    @BeforeClass
    public static void setUp() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        builder.addScript("classpath:/org/cloudfoundry/identity/uaa/schema-hsqldb.sql");
        database = builder.build();
        JdbcTemplate jdbcTemplate = new JdbcTemplate(database);
        dao = new JdbcScimUserProvisioning(jdbcTemplate);
        dao.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
        dao.setPasswordValidator(new NullPasswordValidator());
        ScimSearchQueryConverter filterConverter = new ScimSearchQueryConverter();
        Map<String, String> replaceWith = new HashMap<String, String>();
        replaceWith.put("emails\\.value", "email");
        replaceWith.put("groups\\.display", "authorities");
        replaceWith.put("phoneNumbers\\.value", "phoneNumber");
        filterConverter.setAttributeNameMapper(new SimpleAttributeNameMapper(replaceWith));
        dao.setQueryConverter(filterConverter);
        endpoints = new ScimUserEndpoints();
        endpoints.setScimUserProvisioning(dao);
        mm = new JdbcScimGroupMembershipManager(jdbcTemplate);
        mm.setScimUserProvisioning(dao);
        JdbcScimGroupProvisioning gdao = new JdbcScimGroupProvisioning(jdbcTemplate);
        mm.setScimGroupProvisioning(gdao);
        endpoints.setScimGroupMembershipManager(mm);
        groupEndpoints = new ScimGroupEndpoints(gdao, mm);
        joel = new ScimUser(null, "jdsa", "Joel", "D'sa");
        joel.addEmail("jdsa@vmware.com");
        dale = new ScimUser(null, "olds", "Dale", "Olds");
        dale.addEmail("olds@vmware.com");
        joel = dao.createUser(joel, "password");
        dale = dao.createUser(dale, "password");
    }

    @AfterClass
    public static void tearDown() throws Exception {
        TestUtils.deleteFrom(database, "users", "groups", "group_membership");
        if (database != null) {
            database.shutdown();
        }
    }

    @After
    public void cleanUp() {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(database);
        for (ScimUser user : createdUsers) {
            jdbcTemplate.update("delete from users where id=?", user.getId());
        }
        for (ScimGroup g : createdGroups) {
            jdbcTemplate.update("delete from groups where id=?", g.getId());
            jdbcTemplate.update("delete from group_membership where group_id=?", g.getId());
        }
    }

    private void validateUserGroups(ScimUser user, String... gnm) {
        Set<String> expectedAuthorities = new HashSet<String>();
        expectedAuthorities.addAll(Arrays.asList(gnm));
        expectedAuthorities.add("uaa.user");
        assertNotNull(user.getGroups());
        Log logger = LogFactory.getLog(getClass());
        logger.debug("user's groups: " + user.getGroups() + ", expecting: " + expectedAuthorities);
        assertEquals(expectedAuthorities.size(), user.getGroups().size());
        for (ScimUser.Group g : user.getGroups()) {
            assertTrue(expectedAuthorities.contains(g.getDisplay()));
        }
    }

    @Test
    public void groupsIsSyncedCorrectlyOnCreate() {
        // uaa.user group must already exist before all users are automatically enrolled in it
        createdGroups.add(groupEndpoints.createGroup(new ScimGroup("uaa.user")));

        ScimUser user = new ScimUser(null, "dave", "David", "Syer");
        user.addEmail("dsyer@vmware.com");
        user.setGroups(Arrays.asList(new ScimUser.Group(null, "test1")));
        endpoints.setSecurityContextAccessor(mockSecurityContext(user));
        ScimUser created = endpoints.createUser(user);
        createdUsers.add(created);

        validateUserGroups(created, "uaa.user");
    }

    @Test
    public void groupsIsSyncedCorrectlyOnUpdate() {
        // uaa.user group must already exist before all users are automatically enrolled in it
        createdGroups.add(groupEndpoints.createGroup(new ScimGroup("uaa.user")));

        ScimUser user = new ScimUser(null, "dave", "David", "Syer");
        user.addEmail("dsyer@vmware.com");
        endpoints.setSecurityContextAccessor(mockSecurityContext(user));
        ScimUser created = endpoints.createUser(user);
        createdUsers.add(created);
        validateUserGroups(created, "uaa.user");

        created.setGroups(Arrays.asList(new ScimUser.Group(null, "test1")));
        ScimUser updated = endpoints.updateUser(created, created.getId(), "*");
        validateUserGroups(updated, "uaa.user");
    }

    @Test
    public void groupsIsSyncedCorrectlyOnGet() {
        // uaa.user group must already exist before all users are automatically enrolled in it
        createdGroups.add(groupEndpoints.createGroup(new ScimGroup("uaa.user")));

        ScimUser user = new ScimUser(null, "dave", "David", "Syer");
        user.addEmail("dsyer@vmware.com");
        endpoints.setSecurityContextAccessor(mockSecurityContext(user));
        ScimUser created = endpoints.createUser(user);
        createdUsers.add(created);

        validateUserGroups(created, "uaa.user");

        ScimGroup g = new ScimGroup("test1");
        g.setMembers(Arrays.asList(new ScimGroupMember(created.getId())));
        createdGroups.add(groupEndpoints.createGroup(g));

        validateUserGroups(endpoints.getUser(created.getId()), "test1");
    }

    @Test
    public void userGetsADefaultPassword() {
        ScimUser user = new ScimUser(null, "dave", "David", "Syer");
        user.addEmail("dsyer@vmware.com");
        endpoints.setSecurityContextAccessor(mockSecurityContext(user));
        ScimUser created = endpoints.createUser(user);
        createdUsers.add(created);
        assertNull("A newly created user revealed its password", created.getPassword());
        JdbcTemplate jdbcTemplate = new JdbcTemplate(database);
        String password = jdbcTemplate.queryForObject("select password from users where id=?", String.class,
                created.getId());
        // Generated password...
        assertNotNull(password);
    }

    @Test
    public void userWithNoEmailNotAllowed() {
        ScimUser user = new ScimUser(null, "dave", "David", "Syer");
        endpoints.setSecurityContextAccessor(mockSecurityContext(user));
        try {
            endpoints.createUser(user);
            fail("Expected InvalidScimResourceException");
        } catch (InvalidScimResourceException e) {
            // expected
            String message = e.getMessage();
            assertTrue("Wrong message: " + message, message.contains("email"));
        }
        JdbcTemplate jdbcTemplate = new JdbcTemplate(database);
        int count = jdbcTemplate.queryForInt("select count(*) from users where userName=?", "dave");
        assertEquals(0, count);
    }

    @Test
    public void testHandleExceptionWithConstraintViolation() throws Exception {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        endpoints.setMessageConverters(new HttpMessageConverter<?>[] { new ExceptionReportHttpMessageConverter() });
        View view = endpoints.handleException(new DataIntegrityViolationException("foo"), request);
        ConvertingExceptionView converted = (ConvertingExceptionView) view;
        converted.render(Collections.<String, Object>emptyMap(), request, response);
        String body = response.getContentAsString();
        assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
        // System.err.println(body);
        assertTrue("Wrong body: " + body, body.contains("message\":\"foo"));
    }

    @Test
    public void testHandleExceptionWithBadFieldName() throws Exception {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        endpoints.setMessageConverters(new HttpMessageConverter<?>[] { new ExceptionReportHttpMessageConverter() });
        View view = endpoints.handleException(new HttpMessageConversionException("foo"), request);
        ConvertingExceptionView converted = (ConvertingExceptionView) view;
        converted.render(Collections.<String, Object>emptyMap(), request, response);
        String body = response.getContentAsString();
        assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
        // System.err.println(body);
        assertTrue("Wrong body: " + body, body.contains("message\":\"foo"));
    }

    @Test
    public void userCanInitializePassword() {
        ScimUser user = new ScimUser(null, "dave", "David", "Syer");
        user.addEmail("dsyer@vmware.com");
        ReflectionTestUtils.setField(user, "password", "foo");
        endpoints.setSecurityContextAccessor(mockSecurityContext(user));
        ScimUser created = endpoints.createUser(user);
        createdUsers.add(created);
        assertNull("A newly created user revealed its password", created.getPassword());
        JdbcTemplate jdbcTemplate = new JdbcTemplate(database);
        String password = jdbcTemplate.queryForObject("select password from users where id=?", String.class,
                created.getId());
        assertEquals("foo", password);
    }

    @Test
    public void userCanChangeTheirOwnPasswordIfTheySupplyCorrectCurrentPassword() {
        endpoints.setSecurityContextAccessor(mockSecurityContext(joel));
        PasswordChangeRequest change = new PasswordChangeRequest();
        change.setOldPassword("password");
        change.setPassword("newpassword");
        endpoints.changePassword(joel.getId(), change);
    }

    @Test(expected = ScimException.class)
    public void userCantChangeAnotherUsersPassword() {
        endpoints.setSecurityContextAccessor(mockSecurityContext(joel));
        PasswordChangeRequest change = new PasswordChangeRequest();
        change.setOldPassword("password");
        change.setPassword("newpassword");
        endpoints.changePassword(dale.getId(), change);
    }

    @Test
    public void adminCanChangeAnotherUsersPassword() {
        SecurityContextAccessor sca = mockSecurityContext(dale);
        when(sca.isAdmin()).thenReturn(true);
        endpoints.setSecurityContextAccessor(sca);
        PasswordChangeRequest change = new PasswordChangeRequest();
        change.setPassword("newpassword");
        endpoints.changePassword(joel.getId(), change);
    }

    @Test(expected = ScimException.class)
    public void changePasswordRequestFailsForUserWithoutCurrentPassword() {
        endpoints.setSecurityContextAccessor(mockSecurityContext(joel));
        PasswordChangeRequest change = new PasswordChangeRequest();
        change.setPassword("newpassword");
        endpoints.changePassword(joel.getId(), change);
    }

    @Test(expected = ScimException.class)
    public void changePasswordRequestFailsForAdminWithoutOwnCurrentPassword() {
        endpoints.setSecurityContextAccessor(mockSecurityContext(joel));
        PasswordChangeRequest change = new PasswordChangeRequest();
        change.setPassword("newpassword");
        endpoints.changePassword(joel.getId(), change);
    }

    @Test
    public void clientCanChangeUserPasswordWithoutCurrentPassword() {
        SecurityContextAccessor sca = mockSecurityContext(joel);
        when(sca.isClient()).thenReturn(true);
        endpoints.setSecurityContextAccessor(sca);
        PasswordChangeRequest change = new PasswordChangeRequest();
        change.setPassword("newpassword");
        endpoints.changePassword(joel.getId(), change);
    }

    @Test(expected = BadCredentialsException.class)
    public void changePasswordFailsForUserIfTheySupplyWrongCurrentPassword() {
        endpoints.setSecurityContextAccessor(mockSecurityContext(joel));
        PasswordChangeRequest change = new PasswordChangeRequest();
        change.setPassword("newpassword");
        change.setOldPassword("wrongpassword");
        endpoints.changePassword(joel.getId(), change);
    }

    @Test
    public void deleteIsAllowedWithCorrectVersionInEtag() {
        ScimUser exGuy = new ScimUser(null, "deleteme", "Expendable", "Guy");
        exGuy.addEmail("exguy@imonlyheretobedeleted.com");
        exGuy = dao.createUser(exGuy, "exguyspassword");
        createdUsers.add(exGuy);
        endpoints.deleteUser(exGuy.getId(), Integer.toString(exGuy.getMeta().getVersion()));
    }

    @Test(expected = OptimisticLockingFailureException.class)
    public void deleteIsNotAllowedWithWrongVersionInEtag() {
        ScimUser exGuy = new ScimUser(null, "deleteme2", "Expendable", "Guy");
        exGuy.addEmail("exguy2@imonlyheretobedeleted.com");
        exGuy = dao.createUser(exGuy, "exguyspassword");
        createdUsers.add(exGuy);
        endpoints.deleteUser(exGuy.getId(), Integer.toString(exGuy.getMeta().getVersion() + 1));
    }

    @Test
    public void deleteIsAllowedWithNullEtag() {
        ScimUser exGuy = new ScimUser(null, "deleteme3", "Expendable", "Guy");
        exGuy.addEmail("exguy3@imonlyheretobedeleted.com");
        exGuy = dao.createUser(exGuy, "exguyspassword");
        createdUsers.add(exGuy);
        endpoints.deleteUser(exGuy.getId(), null);
    }

    @Test
    public void deleteUserUpdatesGroupMembership() {
        ScimUser exGuy = new ScimUser(null, "deleteme3", "Expendable", "Guy");
        exGuy.addEmail("exguy3@imonlyheretobedeleted.com");
        exGuy = dao.createUser(exGuy, "exguyspassword");
        createdUsers.add(exGuy);

        ScimGroup g = new ScimGroup("test1");
        g.setMembers(Arrays.asList(new ScimGroupMember(exGuy.getId())));
        g = groupEndpoints.createGroup(g);
        createdGroups.add(g);
        validateGroupMembers(g, exGuy.getId(), true);

        endpoints.deleteUser(exGuy.getId(), "*");
        validateGroupMembers(groupEndpoints.getGroup(g.getId()), exGuy.getId(), false);
    }

    private void validateGroupMembers(ScimGroup g, String mId, boolean expected) {
        boolean isMember = false;
        for (ScimGroupMember m : g.getMembers()) {
            if (mId.equals(m.getMemberId())) {
                isMember = true;
                break;
            }
        }
        assertEquals(expected, isMember);
    }

    private SecurityContextAccessor mockSecurityContext(ScimUser user) {
        SecurityContextAccessor sca = mock(SecurityContextAccessor.class);
        String id = user.getId();
        when(sca.getUserId()).thenReturn(id);
        return sca;
    }

    @Test
    public void testFindAllIds() {
        SearchResults<?> results = endpoints.findUsers("id", "id pr", null, "ascending", 1, 100);
        assertEquals(2, results.getTotalResults());
    }

    @Test
    public void testFindPageOfIds() {
        SearchResults<?> results = endpoints.findUsers("id", "id pr", null, "ascending", 1, 1);
        assertEquals(2, results.getTotalResults());
        assertEquals(1, results.getResources().size());
    }

    @Test
    public void testFindAllNames() {
        SearchResults<?> results = endpoints.findUsers("userName", "id pr", null, "ascending", 1, 100);
        Collection<Object> values = getSetFromMaps(results.getResources(), "userName");
        assertTrue(values.contains("olds"));
    }

    @Test
    public void testFindAllEmails() {
        SearchResults<?> results = endpoints.findUsers("emails.value", "id pr", null, "ascending", 1, 100);
        Collection<Object> values = getSetFromMaps(results.getResources(), "emails.value");
        assertTrue(values.contains(Arrays.asList("olds@vmware.com")));
    }

    @Test
    public void testInvalidFilterExpression() {
        expected.expect(ScimException.class);
        expected.expectMessage(containsString("Invalid filter"));
        SearchResults<?> results = endpoints.findUsers("id", "userName qq 'd'", null, "ascending", 1, 100);
        assertEquals(0, results.getTotalResults());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testFindIdsByUserName() {
        SearchResults<?> results = endpoints.findUsers("id", "userName eq 'jdsa'", null, "ascending", 1, 100);
        assertEquals(1, results.getTotalResults());
        assertEquals(1, results.getSchemas().size()); // System.err.println(results.getValues());
        assertEquals(joel.getId(), ((Map<String, Object>) results.getResources().iterator().next()).get("id"));
    }

    @Test
    public void testFindIdsByUserNameContains() {
        SearchResults<?> results = endpoints.findUsers("id", "userName co 'd'", null, "ascending", 1, 100);
        assertEquals(2, results.getTotalResults());
        assertTrue("Couldn't find id: " + results.getResources(),
                getSetFromMaps(results.getResources(), "id").contains(joel.getId()));
    }

    @Test
    @Ignore
    public void testFindIdsByNameExists() {
        SearchResults<?> results = endpoints.findUsers("id", "name pr", null, "ascending", 1, 100);
        assertEquals(2, results.getTotalResults());
    }

    @Test
    public void testFindIdsByUserNameStartWith() {
        SearchResults<?> results = endpoints.findUsers("id", "userName sw 'j'", null, "ascending", 1, 100);
        assertEquals(1, results.getTotalResults());
        assertTrue("Couldn't find id: " + results.getResources(),
                getSetFromMaps(results.getResources(), "id").contains(joel.getId()));
    }

    @Test
    public void testFindIdsByEmailContains() {
        SearchResults<?> results = endpoints.findUsers("id", "emails.value sw 'j'", null, "ascending", 1, 100);
        assertEquals(1, results.getTotalResults());
        assertTrue("Couldn't find id: " + results.getResources(),
                getSetFromMaps(results.getResources(), "id").contains(joel.getId()));
    }

    @Test
    public void testFindIdsByEmailContainsWithEmptyResult() {
        SearchResults<?> results = endpoints.findUsers("id", "emails.value sw 'z'", null, "ascending", 1, 100);
        assertEquals(0, results.getTotalResults());
    }

    @Test
    public void testFindIdsWithBooleanExpression() {
        SearchResults<?> results = endpoints.findUsers("id", "userName co 'd' and id pr", null, "ascending", 1,
                100);
        assertEquals(2, results.getTotalResults());
        assertTrue("Couldn't find id: " + results.getResources(),
                getSetFromMaps(results.getResources(), "id").contains(joel.getId()));
    }

    @Test
    public void testFindIdsWithBooleanExpressionIvolvingEmails() {
        SearchResults<?> results = endpoints.findUsers("id", "userName co 'd' and emails.value co 'vmware'", null,
                "ascending", 1, 100);
        assertEquals(2, results.getTotalResults());
        assertTrue("Couldn't find id: " + results.getResources(),
                getSetFromMaps(results.getResources(), "id").contains(joel.getId()));
    }

    @SuppressWarnings("unchecked")
    private Collection<Object> getSetFromMaps(Collection<?> resources, String key) {
        Collection<Object> result = new ArrayList<Object>();
        for (Object map : resources) {
            result.add(((Map<String, Object>) map).get(key));
        }
        return result;
    }
}