/*
* Copyright (c) 2010-2011 Lockheed Martin Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* 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.eurekastreams.server.action.authorization.stream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.PersistenceException;
import org.eurekastreams.commons.actions.AuthorizationStrategy;
import org.eurekastreams.commons.actions.context.Principal;
import org.eurekastreams.commons.actions.context.service.ServiceActionContext;
import org.eurekastreams.commons.exceptions.AuthorizationException;
import org.eurekastreams.server.domain.EntityType;
import org.eurekastreams.server.domain.stream.ActivityDTO;
import org.eurekastreams.server.domain.stream.StreamEntityDTO;
import org.eurekastreams.server.persistence.mappers.DomainMapper;
import org.eurekastreams.server.persistence.mappers.GetAllPersonIdsWhoHaveGroupCoordinatorAccess;
import org.eurekastreams.server.persistence.mappers.stream.GetDomainGroupsByShortNames;
import org.eurekastreams.server.search.modelview.DomainGroupModelView;
import org.eurekastreams.server.search.modelview.PersonModelView;
import org.eurekastreams.server.service.actions.strategies.ActivityInteractionType;
import org.eurekastreams.server.service.actions.strategies.activity.ActivityDTOFromParamsStrategy;
import org.eurekastreams.server.service.actions.strategies.activity.ActorRetrievalStrategy;
import org.jmock.Expectations;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
/**
* Test fixture for ActivityAuthorizationStrategy.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class ActivityAuthorizationStrategyTest
{
/**
* Mocking context.
*/
private final JUnit4Mockery mockContext = new JUnit4Mockery()
{
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};
/** Test data. */
private static final String PERSONAL_STREAM_UNIQUEID = "jdoe";
/** Test data. */
private static final String GROUP_STREAM_UNIQUEID = "thegroup";
/** Actor's person ID. */
private static final long ACTOR_ID = 5L;
/** Actor's account ID. */
private static final String ACTOR_ACCOUNT_ID = "actor";
/** Group id. */
private static final long GROUP_ID = 6L;
/** Fixture: stream (personal or group). */
private final StreamEntityDTO streamDTO = mockContext.mock(StreamEntityDTO.class, "streamDTO");
/**
* Groups by shortName DAO.
*/
private final GetDomainGroupsByShortNames groupByShortNameDAO = mockContext
.mock(GetDomainGroupsByShortNames.class);
/**
* Mapper to get the personmodelview by account id.
*/
private final DomainMapper<String, PersonModelView> getPersonModelViewByAccountIdMapper = mockContext
.mock(DomainMapper.class);
/**
* Group follower ids DAO.
*/
private final DomainMapper<Long, List<Long>> groupFollowersDAO = mockContext.mock(DomainMapper.class,
"groupFollowersDAO");
/**
* ActivityDTO.
*/
private final ActivityDTO activityDTO = mockContext.mock(ActivityDTO.class);
/**
* Service action context.
*/
private final ServiceActionContext serviceActionContext = mockContext.mock(ServiceActionContext.class);
/**
* user details.
*/
private final Principal userPrincipal = mockContext.mock(Principal.class);
/**
* DomainGroupModelView.
*/
private final DomainGroupModelView groupDTO = mockContext.mock(DomainGroupModelView.class);
/**
* PersonModelView.
*/
private final PersonModelView personDTO = mockContext.mock(PersonModelView.class);
/**
* Actor retrieval strategy mock.
*/
private final ActorRetrievalStrategy actorRetrievalStrat = mockContext.mock(ActorRetrievalStrategy.class);
/** List of coordinators / members. */
private static final List<Long> PERSON_ID_LIST_WITH_ACTOR = Collections
.unmodifiableList(Arrays.asList(1L, 5L, 9L));
/** List of coordinators / members. */
private static final List<Long> PERSON_ID_LIST_WITHOUT_ACTOR = Collections.unmodifiableList(Arrays.asList(1L, 2L));
/** List of coordinators / members. */
private static final Set<Long> PERSON_ID_SET_WITH_ACTOR = Collections.unmodifiableSet(new HashSet(
PERSON_ID_LIST_WITH_ACTOR));
/** List of coordinators / members. */
private static final Set<Long> PERSON_ID_SET_WITHOUT_ACTOR = Collections.unmodifiableSet(new HashSet(
PERSON_ID_LIST_WITHOUT_ACTOR));
/**
* The Mock for getting all coordinators.
*/
private final GetAllPersonIdsWhoHaveGroupCoordinatorAccess coordinatorMapperMock = mockContext
.mock(GetAllPersonIdsWhoHaveGroupCoordinatorAccess.class);
/**
* Mock instance of {@link ActivityDTOFromParamsStrategy} for retrieving the activity dto from the params.
*/
private final ActivityDTOFromParamsStrategy activityDTOStrategyMock = mockContext
.mock(ActivityDTOFromParamsStrategy.class);
/**
* Activity id.
*/
private final Long activityId = new Long(38271);
/**
* System under test.
*/
private ActivityAuthorizationStrategy sut;
/**
* Setup before each test.
*/
@Before
public void setUp()
{
sut = null; // must set on a per-test basis, since the type of action (post, etc.) is a constructor parameter
mockContext.checking(new Expectations()
{
{
allowing(serviceActionContext).getPrincipal();
will(returnValue(userPrincipal));
oneOf(serviceActionContext).getParams();
will(returnValue(activityId));
}
});
}
/* -------- Personal stream tests -------- */
/**
* Common setup for person tests.
*
* @param activityInteractionType
* Type of action being taken (post, comment, etc.).
* @throws Exception
* Won't.
*/
private void setupPersonTest(final ActivityInteractionType activityInteractionType) throws Exception
{
sut = new ActivityAuthorizationStrategy(groupByShortNameDAO, groupFollowersDAO, actorRetrievalStrat,
coordinatorMapperMock, activityDTOStrategyMock, activityInteractionType,
getPersonModelViewByAccountIdMapper);
mockContext.checking(new Expectations()
{
{
oneOf(activityDTOStrategyMock).execute(userPrincipal, activityId);
will(returnValue(activityDTO));
allowing(activityDTO).getDestinationStream();
will(returnValue(streamDTO));
allowing(streamDTO).getType();
will(returnValue(EntityType.PERSON));
oneOf(actorRetrievalStrat).getActorAccountId(userPrincipal, activityDTO);
will(returnValue(ACTOR_ACCOUNT_ID));
}
});
}
/**
* Test: post to personal stream by owner.
*
* @throws Exception
* Won't.
*/
@Test
public void testAuthorizePostPersonalByOwner() throws Exception
{
setupPersonTest(ActivityInteractionType.POST);
mockContext.checking(new Expectations()
{
{
allowing(streamDTO).getUniqueIdentifier();
will(returnValue(ACTOR_ACCOUNT_ID));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/**
* Common behavior of all personal stream by non-owner tests.
*
* @param activityInteractionType
* Type of action being performed.
* @throws Exception
* If not authorized.
*/
private void corePersonalNonOwnerTest(final ActivityInteractionType activityInteractionType) throws Exception
{
setupPersonTest(activityInteractionType);
mockContext.checking(new Expectations()
{
{
allowing(streamDTO).getUniqueIdentifier();
will(returnValue(PERSONAL_STREAM_UNIQUEID));
oneOf(getPersonModelViewByAccountIdMapper).execute(PERSONAL_STREAM_UNIQUEID);
will(returnValue(personDTO));
}
});
((AuthorizationStrategy) sut).authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/**
* Test: post to personal stream by non-owner.
*
* @throws Exception
* Won't.
*/
@Test
public void testAuthorizePostPersonal() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(personDTO).isStreamPostable();
will(returnValue(true));
}
});
corePersonalNonOwnerTest(ActivityInteractionType.POST);
}
/**
* Test: post to personal stream by non-owner.
*
* @throws Exception
* Should.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizePostPersonalNotEnabled() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(personDTO).isStreamPostable();
will(returnValue(false));
}
});
corePersonalNonOwnerTest(ActivityInteractionType.POST);
}
/**
* Test: comment to personal stream by non-owner.
*
* @throws Exception
* Won't.
*/
@Test
public void testAuthorizeCommentPersonal() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(personDTO).isCommentable();
will(returnValue(true));
}
});
corePersonalNonOwnerTest(ActivityInteractionType.COMMENT);
}
/**
* Test: comment to personal stream by non-owner.
*
* @throws Exception
* Should.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizeCommentPersonalNotEnabled() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(personDTO).isCommentable();
will(returnValue(false));
}
});
corePersonalNonOwnerTest(ActivityInteractionType.COMMENT);
}
/**
* Test: view personal stream by non-owner.
*
* @throws Exception
* Won't.
*/
@Test
public void testAuthorizeViewPersonal() throws Exception
{
corePersonalNonOwnerTest(ActivityInteractionType.VIEW);
}
/* -------- Group stream tests -------- */
/**
* Common setup for group tests.
*
* @param activityInteractionType
* Type of action being taken (post, comment, etc.).
* @throws Exception
* Won't.
*/
private void setupGroupTest(final ActivityInteractionType activityInteractionType) throws Exception
{
sut = new ActivityAuthorizationStrategy(groupByShortNameDAO, groupFollowersDAO, actorRetrievalStrat,
coordinatorMapperMock, activityDTOStrategyMock, activityInteractionType,
getPersonModelViewByAccountIdMapper);
mockContext.checking(new Expectations()
{
{
oneOf(activityDTOStrategyMock).execute(userPrincipal, activityId);
will(returnValue(activityDTO));
allowing(activityDTO).getDestinationStream();
will(returnValue(streamDTO));
allowing(streamDTO).getUniqueIdentifier();
will(returnValue(GROUP_STREAM_UNIQUEID));
allowing(streamDTO).getType();
will(returnValue(EntityType.GROUP));
oneOf(groupByShortNameDAO).execute(with(equal(Collections.singletonList(GROUP_STREAM_UNIQUEID))));
will(returnValue(Collections.singletonList(groupDTO)));
allowing(groupDTO).getId();
will(returnValue(GROUP_ID));
oneOf(actorRetrievalStrat).getActorId(userPrincipal, activityDTO);
will(returnValue(ACTOR_ID));
}
});
}
/* ---- Public group, action by anybody ---- */
/**
* Common behavior of all public group stream by non-coordinator tests.
*
* @param activityInteractionType
* Type of action being performed.
* @throws Exception
* If not authorized.
*/
private void corePublicGroupTest(final ActivityInteractionType activityInteractionType) throws Exception
{
setupGroupTest(activityInteractionType);
mockContext.checking(new Expectations()
{
{
oneOf(coordinatorMapperMock).execute(GROUP_ID);
will(returnValue(PERSON_ID_SET_WITHOUT_ACTOR));
allowing(groupDTO).isPublic();
will(returnValue(true));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/**
* Test: post to public group, user not coordinator.
*
* @throws Exception
* Won't.
*/
@Test
public void testAuthorizePostPublicGroup() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(groupDTO).isStreamPostable();
will(returnValue(true));
}
});
corePublicGroupTest(ActivityInteractionType.POST);
}
/**
* Test: post to public group, user not coordinator, not postable.
*
* @throws Exception
* Should.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizePostPublicGroupNotEnabled() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(groupDTO).isStreamPostable();
will(returnValue(false));
}
});
corePublicGroupTest(ActivityInteractionType.POST);
}
/**
* Test: comment to public group, user not coordinator.
*
* @throws Exception
* Won't.
*/
@Test
public void testAuthorizeCommentPublicGroup() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(groupDTO).isCommentable();
will(returnValue(true));
}
});
corePublicGroupTest(ActivityInteractionType.COMMENT);
}
/**
* Test: comment to public group, user not coordinator, not commentable.
*
* @throws Exception
* Should.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizeCommentPublicGroupNotEnabled() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(groupDTO).isCommentable();
will(returnValue(false));
}
});
corePublicGroupTest(ActivityInteractionType.COMMENT);
}
/**
* Test: comment to public group, user not coordinator.
*
* @throws Exception
* Won't.
*/
@Test
public void testAuthorizeViewPublicGroup() throws Exception
{
corePublicGroupTest(ActivityInteractionType.VIEW);
}
/**
* Test: unspecified action to public group, user not coordinator.
*
* @throws Exception
* Should.
*/
@Test(expected = RuntimeException.class)
public void testAuthorizeOtherActionPublicGroup() throws Exception
{
corePublicGroupTest(ActivityInteractionType.NOTSET);
}
/* ---- Public group, action by coordinator ---- */
/**
* Test: post to public group, user is coordinator.
*
* @throws Exception
* Won't.
*/
@Test
public void testAuthorizePostPublicGroupCoordinator() throws Exception
{
setupGroupTest(ActivityInteractionType.POST);
mockContext.checking(new Expectations()
{
{
oneOf(coordinatorMapperMock).execute(GROUP_ID);
will(returnValue(PERSON_ID_SET_WITH_ACTOR));
allowing(groupDTO).isPublic();
will(returnValue(true));
// refactoring would make the following unnecessary
allowing(groupDTO).isStreamPostable();
will(returnValue(false));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/* ---- Private group, action by member ---- */
/**
* Common behavior of all private group stream by member tests.
*
* @param activityInteractionType
* Type of action being performed.
* @throws Exception
* If not authorized.
*/
private void corePrivateMemberGroupTest(final ActivityInteractionType activityInteractionType) throws Exception
{
setupGroupTest(activityInteractionType);
mockContext.checking(new Expectations()
{
{
oneOf(coordinatorMapperMock).execute(GROUP_ID);
will(returnValue(PERSON_ID_SET_WITHOUT_ACTOR));
oneOf(groupFollowersDAO).execute(GROUP_ID);
will(returnValue(PERSON_ID_LIST_WITH_ACTOR));
allowing(groupDTO).isPublic();
will(returnValue(false));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/**
* Test: post to private group, user member.
*
* @throws Exception
* Won't.
*/
@Test
public void testAuthorizePostPrivateGroup() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(groupDTO).isStreamPostable();
will(returnValue(true));
}
});
corePrivateMemberGroupTest(ActivityInteractionType.POST);
}
/**
* Test: post to private group, user member, not postable.
*
* @throws Exception
* Should.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizePostPrivateGroupNotEnabled() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(groupDTO).isStreamPostable();
will(returnValue(false));
}
});
corePrivateMemberGroupTest(ActivityInteractionType.POST);
}
// TODO: The following three tests are ignored because they fail because the code is broken.
/**
* Test: comment to private group, user member.
*
* @throws Exception
* Won't.
*/
@Test
@Ignore
public void testAuthorizeCommentPrivateGroup() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(groupDTO).isCommentable();
will(returnValue(true));
}
});
corePrivateMemberGroupTest(ActivityInteractionType.COMMENT);
}
/**
* Test: comment to private group, user member, not commentable.
*
* @throws Exception
* Should.
*/
@Test(expected = AuthorizationException.class)
@Ignore
public void testAuthorizeCommentPrivateGroupNotEnabled() throws Exception
{
mockContext.checking(new Expectations()
{
{
allowing(groupDTO).isCommentable();
will(returnValue(false));
}
});
corePrivateMemberGroupTest(ActivityInteractionType.COMMENT);
}
/**
* Test: comment to private group, user member.
*
* @throws Exception
* Won't.
*/
@Test
@Ignore
public void testAuthorizeViewPrivateGroup() throws Exception
{
corePrivateMemberGroupTest(ActivityInteractionType.VIEW);
}
/* ---- Other private group ---- */
/**
* Test: comment to private group, user coordinator.
*
* @throws Exception
* Won't.
*/
@Test
public void testAuthorizeViewPrivateGroupCoordinator() throws Exception
{
setupGroupTest(ActivityInteractionType.VIEW);
mockContext.checking(new Expectations()
{
{
oneOf(coordinatorMapperMock).execute(GROUP_ID);
will(returnValue(PERSON_ID_SET_WITH_ACTOR));
allowing(groupDTO).isPublic();
will(returnValue(false));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/**
* Test: comment to private group, user not member.
*
* @throws Exception
* Should.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizeViewPrivateGroupNonMember() throws Exception
{
setupGroupTest(ActivityInteractionType.VIEW);
mockContext.checking(new Expectations()
{
{
oneOf(coordinatorMapperMock).execute(GROUP_ID);
will(returnValue(PERSON_ID_SET_WITHOUT_ACTOR));
oneOf(groupFollowersDAO).execute(GROUP_ID);
will(returnValue(PERSON_ID_LIST_WITHOUT_ACTOR));
allowing(groupDTO).isPublic();
will(returnValue(false));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/* -------- Error condition tests -------t- */
/**
* Creates the SUT for posting activities.
*/
private void createSutForPost()
{
sut = new ActivityAuthorizationStrategy(groupByShortNameDAO, groupFollowersDAO, actorRetrievalStrat,
coordinatorMapperMock, activityDTOStrategyMock, ActivityInteractionType.POST,
getPersonModelViewByAccountIdMapper);
}
/**
* Test: error retrieving activity.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizeErrorRetrievingActivity()
{
createSutForPost();
mockContext.checking(new Expectations()
{
{
oneOf(activityDTOStrategyMock).execute(userPrincipal, activityId);
will(throwException(new PersistenceException()));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/**
* Test: error retrieving actor for personal stream posting.
*
* @throws Exception
* Should only throw AuthorizationException.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizeErrorRetrievingActor() throws Exception
{
createSutForPost();
mockContext.checking(new Expectations()
{
{
oneOf(activityDTOStrategyMock).execute(userPrincipal, activityId);
will(returnValue(activityDTO));
allowing(activityDTO).getDestinationStream();
will(returnValue(streamDTO));
allowing(streamDTO).getType();
will(returnValue(EntityType.PERSON));
allowing(streamDTO).getUniqueIdentifier();
will(returnValue(PERSONAL_STREAM_UNIQUEID));
oneOf(actorRetrievalStrat).getActorAccountId(userPrincipal, activityDTO);
will(throwException(new PersistenceException()));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/**
* Test: error retrieving group for group stream posting.
*
* @throws Exception
* Should only throw AuthorizationException.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizeErrorRetrievingGroup() throws Exception
{
createSutForPost();
mockContext.checking(new Expectations()
{
{
oneOf(activityDTOStrategyMock).execute(userPrincipal, activityId);
will(returnValue(activityDTO));
allowing(activityDTO).getDestinationStream();
will(returnValue(streamDTO));
allowing(streamDTO).getType();
will(returnValue(EntityType.GROUP));
allowing(streamDTO).getUniqueIdentifier();
will(returnValue(GROUP_STREAM_UNIQUEID));
oneOf(groupByShortNameDAO).execute(with(equal(Collections.singletonList(GROUP_STREAM_UNIQUEID))));
will(throwException(new PersistenceException()));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/* -------- Coverage tests -------- */
/**
* Test: DAO returns a null activity.
*/
@Test
public void testAuthorizeNullActivityDTOParamsRetrieval()
{
createSutForPost();
mockContext.checking(new Expectations()
{
{
oneOf(activityDTOStrategyMock).execute(userPrincipal, activityId);
will(returnValue(null));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/**
* Test: invalid stream type for activity.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizeActivityDestinationInvalid()
{
createSutForPost();
mockContext.checking(new Expectations()
{
{
oneOf(activityDTOStrategyMock).execute(userPrincipal, activityId);
will(returnValue(activityDTO));
allowing(activityDTO).getDestinationStream();
will(returnValue(streamDTO));
oneOf(streamDTO).getType();
will(returnValue(EntityType.ORGANIZATION));
}
});
sut.authorize(serviceActionContext);
mockContext.assertIsSatisfied();
}
/**
* Test: invalid action type.
*
* @throws Exception
* Should only throw AuthorizationException.
*/
@Test(expected = AuthorizationException.class)
public void testAuthorizeInvalidActionType() throws Exception
{
corePublicGroupTest(ActivityInteractionType.NOTSET);
}
}