/**
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you 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 the following location:
 *
 *   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.jasig.portlet.announcements.service;

import java.util.Set;
import javax.portlet.PortletRequest;
import org.jasig.portlet.announcements.UnauthorizedException;
import org.jasig.portlet.announcements.model.Topic;
import org.jasig.portlet.announcements.model.UserRoles;

/**
 * Utility class that can be used statically to check role membership of a Topic, or when
 * instantiated, can be used inside a JSP for example to check role membership as it relates to a
 * specific Topic.
 *
 * @author Erik A. Olsson (eolsson@uci.edu)
 */
public final class UserPermissionChecker implements UserRoles {

  /**
   * This approach to unauthenticated users does not support multiples. In this case, thankfully, it
   * only applies to circumstances where the username 'guest' has been explicitly added to the
   * audience of a topic (which is uncommon). It doesn't interfere with general support for multiple
   * guest users.
   */
  private static final String GUEST_USERNAME = "guest";

  private final boolean admin;
  private final boolean moderator;
  private final boolean author;
  private final boolean audience;
  private final String userName;
  private final boolean guest;

  /**
   * Used by {@link UserPermissionCheckerFactory}. A fully-constructed instance can tell you
   * anything about a user's permissions, but is more expensive to create. If you only need to know
   * if the user is in the audience for a topic, use the static method.
   */
  /* package-private */ UserPermissionChecker(PortletRequest request, Topic topic) {
    guest = (request.getRemoteUser() == null);
    userName = guest ? GUEST_USERNAME : request.getRemoteUser();

    if (request.isUserInRole(UserRoles.PORTAL_ADMIN_ROLE_NAME)
        || inRoleForTopic(request, ADMIN_ROLE_NAME, topic)) {
      admin = moderator = author = true;
    } else if (inRoleForTopic(request, MODERATOR_ROLE_NAME, topic)) {
      admin = false;
      moderator = author = true;
    } else if (inRoleForTopic(request, AUTHOR_ROLE_NAME, topic)) {
      admin = moderator = false;
      author = true;
    } else {
      admin = moderator = author = false;
    }

    audience = inRoleForTopic(request, AUDIENCE_ROLE_NAME, topic);
  }

  /** @return true if the user in the PortletRequest has the role specified by the Topic */
  public static boolean inRoleForTopic(PortletRequest request, String role, Topic topic) {
    boolean isGuest = (request.getRemoteUser() == null);

    // automatic for portal admins, only check if not a guest
    if (!isGuest && UserPermissionChecker.isPortalAdmin(request)) {
      return true;
    }

    String userName = isGuest ? GUEST_USERNAME : request.getRemoteUser();

    Set<String> group = topic.getGroup(role);
    for (String groupMember : group) {
      if (groupMember.startsWith("USER.")) {
        String p[] = groupMember.split("\\.");
        if (p[1].equalsIgnoreCase(userName)) {
          return true;
        }
      } else if (request.isUserInRole(groupMember)) {
        return true;
      }
    }
    return false;
  }

  public static boolean isPortalAdmin(PortletRequest request) {
    return request.isUserInRole(UserRoles.PORTAL_ADMIN_ROLE_NAME);
  }

  public boolean isGuest() {
    return guest;
  }
  /**
   * Used in JSPs.
   *
   * @return the userName
   */
  public String getUserName() {
    return userName;
  }

  /**
   * Used in JSPs.
   *
   * @return the admin
   */
  public boolean isAdmin() {
    return admin;
  }

  /** @return the moderator */
  public boolean isModerator() {
    return moderator;
  }

  /** @return the author */
  public boolean isAuthor() {
    return author;
  }

  /** @return the audience */
  public boolean isAudience() {
    return audience;
  }

  public boolean canEditTopic() {
    return (isAuthor() || isModerator() || isAdmin());
  }

  public void validateCanEditTopic() {
    if (!canEditTopic()) throw new UnauthorizedException("You do not have access to this topic!");
  }
}

@@ -1071,16 +1071,73 @@
 erRoles;
+%0Aimport org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;
 %0A%0A/**%0A *
@@ -1839,16 +1839,73 @@
 %22guest%22;
+%0A  private static final String USERNAME_PREFIX = %22USER.%22;
 %0A%0A  priv
@@ -2095,16 +2095,110 @@
 guest;%0A%0A
+  private static final Logger logger = LoggerFactory.getLogger(UserPermissionChecker.class);%0A%0A
   /**%0A  
@@ -3799,23 +3799,31 @@
 rtsWith(
-%22
 USER
-.%22
+NAME_PREFIX
 )) %7B%0A   
@@ -3866,16 +3866,585 @@
 %22%5C%5C.%22);%0A
+        if (p.length == 1) %7B%0A          /*%0A           * ANNPLT-137:  The evaluation cannot be made if the groupMember String (value in the%0A           * column) is EXACTLY 'USER.';  it's bad data.  We need to capture this fact (and prevent%0A           * a stack trace) or else the topic will become unusable.%0A           */%0A          logger.warn(%22BAD DATA:  Role '%7B%7D' for Topic '%7B%7D' contains a username prefix ('%22%0A                  + USERNAME_PREFIX + %22') without a username;  ignoring this value.%22,%0A                  role, topic.getTitle());%0A          continue;%0A        %7D%0A
         

The string 'USER.' as a role value breaks the Announcements display portlet