package org.jboss.seam.security.permission;

import static org.jboss.seam.ScopeType.APPLICATION;
import static org.jboss.seam.annotations.Install.BUILT_IN;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.jboss.seam.Component;
import org.jboss.seam.Seam;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Startup;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.security.Identity;
import org.jboss.seam.security.Role;
import org.jboss.seam.security.SimplePrincipal;

/**
 * Resolves dynamically-assigned permissions, mapped to a user or a role, and kept in persistent 
 * storage, such as a relational database.
 * 
 * @author Shane Bryzak
 */
@Name("org.jboss.seam.security.persistentPermissionResolver")
@Scope(APPLICATION)
@BypassInterceptors
@Install(precedence=BUILT_IN)
@Startup
public class PersistentPermissionResolver implements PermissionResolver, Serializable
{      
   private PermissionStore permissionStore;
   
   private static final LogProvider log = Logging.getLogProvider(PersistentPermissionResolver.class);   
   
   @Create
   public void create()
   {
      initPermissionStore();
   }
   
   protected void initPermissionStore()
   {
      if (permissionStore == null)
      {
         permissionStore = (PermissionStore) Component.getInstance(JpaPermissionStore.class, true);
      }           
      
      if (permissionStore == null)
      {
         log.warn("no permission store available - please install a PermissionStore with the name '" +
               Seam.getComponentName(JpaPermissionStore.class) + "' if persistent permissions are required.");
      }
   }     
   
   public PermissionStore getPermissionStore()
   {
      return permissionStore;
   }
   
   public void setPermissionStore(PermissionStore permissionStore)
   {
      this.permissionStore = permissionStore;
   }
   
   public boolean hasPermission(Object target, String action)
   {      
      if (permissionStore == null) return false;
      
      Identity identity = Identity.instance();
      
      if (!identity.isLoggedIn()) return false;      
      
      List<Permission> permissions = permissionStore.listPermissions(target, action);
      
      String username = identity.getPrincipal() != null ? identity.getPrincipal().getName() : null;
      
      for (Permission permission : permissions)
      {
         if (username != null && permission.getRecipient() instanceof SimplePrincipal &&
               username.equals(permission.getRecipient().getName()))
         {
            return true;
         }
         
         if (permission.getRecipient() instanceof Role)
         {
            Role role = (Role) permission.getRecipient();
            
            if (role.isConditional())
            {
               RuleBasedPermissionResolver resolver = RuleBasedPermissionResolver.instance();
               if (resolver.checkConditionalRole(role.getName(), target, action)) return true;               
            }
            else if (identity.hasRole(role.getName()))
            {
               return true;
            }
         }
      }      
      
      return false;
   }
   
   public void filterSetByAction(Set<Object> targets, String action)
   {
      if (permissionStore == null) return;
      
      Identity identity = Identity.instance();
      if (!identity.isLoggedIn()) return;
      
      List<Permission> permissions = permissionStore.listPermissions(targets, action);
      
      String username = identity.getPrincipal().getName();
      
      Iterator iter = targets.iterator();
      while (iter.hasNext())
      {
         Object target = iter.next();
         
         for (Permission permission : permissions)
         {
            if (permission.getTarget().equals(target))
            {
               if (permission.getRecipient() instanceof SimplePrincipal &&
                     username.equals(permission.getRecipient().getName()))
               {
                  iter.remove();
                  break;
               }
               
               if (permission.getRecipient() instanceof Role)
               {
                  Role role = (Role) permission.getRecipient();
                  
                  if (role.isConditional())
                  {
                     RuleBasedPermissionResolver resolver = RuleBasedPermissionResolver.instance();
                     if (resolver.checkConditionalRole(role.getName(), target, action))
                     {
                        iter.remove();
                        break;
                     }
                  }
                  else if (identity.hasRole(role.getName()))
                  {
                     iter.remove();
                     break;
                  }
               }               
            }
         }
      }
   }
}
