/* * 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.service.utility.authorization; import java.util.List; import org.apache.commons.logging.Log; import org.eurekastreams.commons.logging.LogFactory; import org.eurekastreams.server.domain.ActivityRestrictionEntity; import org.eurekastreams.server.domain.stream.ActivityDTO; import org.eurekastreams.server.persistence.mappers.DomainMapper; import org.eurekastreams.server.persistence.mappers.GetAllPersonIdsWhoHaveGroupCoordinatorAccess; import org.eurekastreams.server.search.modelview.DomainGroupModelView; import org.eurekastreams.server.search.modelview.PersonModelView; import org.eurekastreams.server.service.actions.strategies.ActivityInteractionType; /** * Authorization Strategy for an Activity - Determines if user has permission to modify (Post|Comment|View on) an * activity. */ public class ActivityInteractionAuthorizationStrategy { // 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. /** Log. */ private final Log log = LogFactory.make(); /** DAO to get person by ID. */ private final DomainMapper<Long, PersonModelView> getPersonByIdDAO; /** DAO to get group by ID. */ private final DomainMapper<Long, DomainGroupModelView> getGroupByIdDAO; /** DAO to get group follower IDs. */ private final DomainMapper<Long, List<Long>> groupFollowersDAO; /** DAO to get all coordinators of a group. */ private final GetAllPersonIdsWhoHaveGroupCoordinatorAccess groupCoordDAO; /** * Constructor. * * @param inGetPersonByIdDAO * DAO to get person by ID. * @param inGetGroupByIdDAO * DAO to get group by ID. * @param inGroupFollowersDAO * DAO to get group follower IDs. * @param inGroupCoordDAO * DAO to get all coordinators of a group. */ public ActivityInteractionAuthorizationStrategy(final DomainMapper<Long, PersonModelView> inGetPersonByIdDAO, final DomainMapper<Long, DomainGroupModelView> inGetGroupByIdDAO, final DomainMapper<Long, List<Long>> inGroupFollowersDAO, final GetAllPersonIdsWhoHaveGroupCoordinatorAccess inGroupCoordDAO) { getPersonByIdDAO = inGetPersonByIdDAO; getGroupByIdDAO = inGetGroupByIdDAO; groupFollowersDAO = inGroupFollowersDAO; groupCoordDAO = inGroupCoordDAO; } /** * Determines if user has permission to interact with (post/comment/view) an activity. * * @param userId * Person ID of user attempting the interaction. * @param activity * Activity being interacted with. * @param interactionType * Type of interaction. * @return true if allowed, false if not. */ public boolean authorize(final long userId, final ActivityDTO activity, final ActivityInteractionType interactionType) { switch (activity.getDestinationStream().getType()) { case PERSON: return authorizeForPersonStream(userId, activity, interactionType); case GROUP: return authorizeForGroupStream(userId, activity, interactionType); case RESOURCE: // anyone can post comment to resource stream activity. return true; default: return false; } } /** * Determines if users in general have permission to interact with (post/comment/view) an activity. * * @param activity * Activity being interacted with. * @param interactionType * Type of interaction. * @param strict * Strict (true) = "are ALL users allowed to do this"; Relaxed (false) = are users who have access to the * activity allowed to do this. For private groups, relaxed assumes the users are members of the group. * @return true if allowed, false if not. */ public boolean authorize(final ActivityDTO activity, final ActivityInteractionType interactionType, final boolean strict) { long entityId = activity.getDestinationStream().getEntityId(); // this is bad style to declare case-specific variables here (variables should be declared in the most limited // scope possible), but checkstyle won't allow blocks in switch cases DomainGroupModelView group; switch (activity.getDestinationStream().getType()) { case PERSON: return isStreamInteractionAuthorized(getPersonByIdDAO.execute(entityId), interactionType); case GROUP: group = getGroupByIdDAO.execute(entityId); return (!strict || group.isPublic()) ? isStreamInteractionAuthorized(group, interactionType) : false; case RESOURCE: // anyone can post comment to resource stream activity. return true; default: return false; } } /** * Determines if the given entity allows the given type of interaction with activities in its stream. * * @param entity * Entity containing a stream. * @param interactionType * Type of interaction. * @return true if allowed, false if not. */ private boolean isStreamInteractionAuthorized(final ActivityRestrictionEntity entity, final ActivityInteractionType interactionType) { switch (interactionType) { case POST: return entity.isStreamPostable(); case COMMENT: return entity.isCommentable(); case VIEW: return true; default: throw new RuntimeException("Unknown/unhandled activity interaction type."); } } /** * Performs authorization for personal streams. * * @param userId * Person ID of user attempting the interaction. * @param activity * Activity being interacted with. * @param interactionType * Type of interaction. * @return true if allowed, false if not. */ private boolean authorizeForPersonStream(final long userId, final ActivityDTO activity, final ActivityInteractionType interactionType) { try { // check if the user is the owner of the stream being posted to final long streamOwnerId = activity.getDestinationStream().getEntityId(); if (streamOwnerId == userId) { return true; } // check if the stream has been authorized for this type of interaction PersonModelView targetStreamOwner = getPersonByIdDAO.execute(streamOwnerId); return isStreamInteractionAuthorized(targetStreamOwner, interactionType); } catch (Exception ex) { log.error("Error occurred authorizing the activity interaction.", ex); return false; } } /** * Performs authorization for group streams. * * @param userId * Person ID of user attempting the interaction. * @param inActivity * Activity being interacted with. * @param interactionType * Type of interaction. * @return true if allowed, false if not. */ private boolean authorizeForGroupStream(final long userId, final ActivityDTO inActivity, final ActivityInteractionType interactionType) { try { final long groupId = inActivity.getDestinationStream().getEntityId(); DomainGroupModelView group = getGroupByIdDAO.execute(groupId); if (group.isPublic()) { // first see if user is allowed to take action in general if (isStreamInteractionAuthorized(group, interactionType)) { return true; } } else { // first see if user is allowed to take action in general, then verify they are a member of the group if (isStreamInteractionAuthorized(group, interactionType) && groupFollowersDAO.execute(groupId).contains(userId)) { return true; } } // if user is a group coordinator or a system admin, they're allowed to do anything // Note that this DAO also includes the list of system admins (not just the specified group coordinators) return groupCoordDAO.execute(groupId).contains(userId); } catch (Exception ex) { log.error("Error occurred authorizing the activity interaction.", ex); return false; } } }