/* * Copyright (c) 2010 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.Collections; import java.util.List; import org.apache.commons.logging.Log; 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.commons.logging.LogFactory; import org.eurekastreams.server.domain.ActivityRestrictionEntity; 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; /** * Authorization Strategy for an Activity - Determines if user has permission to modify (Post|Comment|View on) an * activity. */ public class ActivityAuthorizationStrategy implements AuthorizationStrategy<ServiceActionContext> { // TODO refactoring commenting to be able to tell the activityAuthorization strat that it is a comment. This will // get rid of the two entity mapper and can be replaced with the generic version. /** * Local logger instance. */ private Log logger = LogFactory.make(); /** * Groups by shortName DAO. */ private final GetDomainGroupsByShortNames groupByShortNameDAO; /** * People by accountId DAO. */ private final DomainMapper<String, PersonModelView> getPersonModelViewByAccountIdMapper; /** * Group follower ids DAO. */ private final DomainMapper<Long, List<Long>> groupFollowersDAO; /** * Local instance of actor retrieval strategy for determining who made the request to interact with the activity. */ private final ActorRetrievalStrategy actorRetrievalStrategy; /** * Strategy for getting ActivityDTO from incoming params array. */ @SuppressWarnings("unchecked") private final ActivityDTOFromParamsStrategy activityDTOStrategy; /** * The mapper to get all coordinators of a group. */ private final GetAllPersonIdsWhoHaveGroupCoordinatorAccess groupCoordMapper; /** * The type of Action you are taking on an activity. */ private final ActivityInteractionType type; /** * Constructor. * * @param inGroupByShortNameDAO * Groups by shortName DAO. * @param inGroupFollowersDAO * Group follower ids DAO. * @param inActorRetrievalStrategy * Actor retrieval strategy. * @param inGroupCoordMapper * GetAllPersonIdsWhoHaveGroupCoordinatorAccess mapper instance. * @param inActivityDTOStrategy * ActivityDTOFromParamsStrategy instance. * @param inType * The type of interaction on an activity. * @param inGetPersonModelViewByAccountIdMapper * mapper to get a person modelview by account id */ @SuppressWarnings("unchecked") public ActivityAuthorizationStrategy(final GetDomainGroupsByShortNames inGroupByShortNameDAO, final DomainMapper<Long, List<Long>> inGroupFollowersDAO, final ActorRetrievalStrategy inActorRetrievalStrategy, final GetAllPersonIdsWhoHaveGroupCoordinatorAccess inGroupCoordMapper, final ActivityDTOFromParamsStrategy inActivityDTOStrategy, final ActivityInteractionType inType, final DomainMapper<String, PersonModelView> inGetPersonModelViewByAccountIdMapper) { groupByShortNameDAO = inGroupByShortNameDAO; groupFollowersDAO = inGroupFollowersDAO; actorRetrievalStrategy = inActorRetrievalStrategy; groupCoordMapper = inGroupCoordMapper; activityDTOStrategy = inActivityDTOStrategy; getPersonModelViewByAccountIdMapper = inGetPersonModelViewByAccountIdMapper; type = inType; } /** * Determines if user has permission to modify (Post|Comment|View on) an activity. * * @param inActionContext * the action context */ @SuppressWarnings("unchecked") @Override public void authorize(final ServiceActionContext inActionContext) { ActivityDTO activity = null; try { activity = activityDTOStrategy.execute(inActionContext.getPrincipal(), inActionContext.getParams()); } catch (Exception ex) { logger.error("Error occurred retrieving the activity dto params.", ex); throw new AuthorizationException( "This action could not authorize the request due to failure retrieving parameters.", ex); } if (activity != null) { switch (activity.getDestinationStream().getType()) { case PERSON: performPersonAuthorization(inActionContext.getPrincipal(), activity); break; case GROUP: performGroupAuthorization(inActionContext.getPrincipal(), activity); break; case RESOURCE: // anyone can post comment to resource stream activity. break; default: throw new AuthorizationException("This Action only accepts activities for accepted destination types."); } } } /** * Helper method to perform group authorization. * * @param inUser * - UserDetails of the user making the request. * @param inActivity * - instance of ActivityDTO. */ private void performGroupAuthorization(final Principal inUser, final ActivityDTO inActivity) { try { // get the group info from cache: StreamEntityDTO theStreamScope = inActivity.getDestinationStream(); DomainGroupModelView cachedGroup = groupByShortNameDAO.execute( Collections.singletonList(theStreamScope.getUniqueIdentifier())).get(0); long senderId = actorRetrievalStrategy.getActorId(inUser, inActivity); boolean isUserCoordinator = groupCoordMapper.execute(cachedGroup.getId()).contains(senderId); // if group is public check to see if the current stream interaction is allowed based on configuration, // if so then short-circuit. if (cachedGroup.isPublic()) { if (isStreamInteractionAuthorized(cachedGroup, type) || isUserCoordinator) { return; } else { throw new AuthorizationException("Group is public but the poster is not a " + "coordinator and the group is configured to not allow stream interaction: " + type); } } // The group is private, continue forward testing private group authorization. if (isUserCoordinator) { // user is a coordinator return; } if (groupFollowersDAO.execute(cachedGroup.getId()).contains(senderId) && isStreamInteractionAuthorized(cachedGroup, type)) { // user is a follower return; } } catch (Exception ex) { logger.error("Error occurred authorizing the activity post.", ex); } throw new AuthorizationException("Current user does not have access right to modify activity."); } /** * Returns the Entity's configuration for stream posting or commenting based on the passed in entity context. * * @param inEntity * The entity to check. * @param inInteractionType * The type of interaction to check if the Entity allows. * @return if the entity is restricted. */ private boolean isStreamInteractionAuthorized(final ActivityRestrictionEntity inEntity, final ActivityInteractionType inInteractionType) { switch (inInteractionType) { case POST: return inEntity.isStreamPostable(); case COMMENT: return inEntity.isCommentable(); case VIEW: return true; default: throw new RuntimeException("Type of Activity not set."); } } /** * Helper method to perform Person authorization. * * @param inUser * - UserDetails of the user making the request. * @param inActivity * - instance of ActivityDTO. */ private void performPersonAuthorization(final Principal inUser, final ActivityDTO inActivity) { try { // Check to see if the stream is locked down and if they have permission to post to it. String targetStreamOwnerAccountId = inActivity.getDestinationStream().getUniqueIdentifier(); boolean isActorTheStreamOwner = actorRetrievalStrategy.getActorAccountId(inUser, inActivity).equals( targetStreamOwnerAccountId); // Test if the user is the owner of the stream being posted to if (isActorTheStreamOwner) { return; } PersonModelView targetStreamOwner = getPersonModelViewByAccountIdMapper.execute(targetStreamOwnerAccountId); // or the stream has been authorized for this type of interaction. if (isStreamInteractionAuthorized(targetStreamOwner, type)) { return; } } catch (Exception ex) { logger.error("Error occurred authorizing the activity post.", ex); } throw new AuthorizationException("Current user does not have access rights to modify activity."); } }