Package org.jboss.seam.security

Source Code of org.jboss.seam.security.Identity

package org.jboss.seam.security;

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

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.security.Principal;
import java.security.acl.Group;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
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.annotations.security.Restrict;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Events;
import org.jboss.seam.core.Expressions;
import org.jboss.seam.core.Expressions.MethodExpression;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.persistence.PersistenceProvider;
import org.jboss.seam.util.Strings;
import org.jboss.seam.web.Session;

/**
* API for authorization and authentication via
* Seam security. This base implementation
* supports role-based authorization only.
* Subclasses may add more sophisticated
* permissioning mechanisms.
*
* @author Shane Bryzak
*
*/
@Name("org.jboss.seam.security.identity")
@Scope(SESSION)
@Install(precedence = BUILT_IN)
@BypassInterceptors
@Startup
public class Identity implements Serializable
   public static final String EVENT_LOGIN_SUCCESSFUL = "org.jboss.seam.security.loginSuccessful";
   public static final String EVENT_LOGIN_FAILED = "org.jboss.seam.security.loginFailed";
   public static final String EVENT_NOT_LOGGED_IN = "org.jboss.seam.security.notLoggedIn";
   public static final String EVENT_NOT_AUTHORIZED = "org.jboss.seam.security.notAuthorized";
   public static final String EVENT_PRE_AUTHENTICATE = "org.jboss.seam.security.preAuthenticate";
   public static final String EVENT_POST_AUTHENTICATE = "org.jboss.seam.security.postAuthenticate";
   public static final String EVENT_LOGGED_OUT = "org.jboss.seam.security.loggedOut";
   public static final String EVENT_CREDENTIALS_UPDATED = "org.jboss.seam.security.credentialsUpdated";
   public static final String EVENT_REMEMBER_ME = "org.jboss.seam.security.rememberMe";
  
   protected static boolean securityEnabled = true;
  
   public static final String ROLES_GROUP = "Roles";
  
   private static final String LOGIN_TRIED = "org.jboss.seam.security.loginTried";
  
   private static final long serialVersionUID = 3751659008033189259L;
  
   private static final LogProvider log = Logging.getLogProvider(Identity.class);
  
   private String username;
   private String password;
  
   private MethodExpression authenticateMethod;

   private Principal principal;  
   private Subject subject;
  
   private boolean rememberMe;
  
   private String jaasConfigName = null;
  
   private List<String> preAuthenticationRoles = new ArrayList<String>();
  
   /**
    * Flag that indicates we are in the process of authenticating
    */
   private boolean authenticating = false;
        
   @Create
   public void create()
   {    
      subject = new Subject();
   }
  
   public static boolean isSecurityEnabled()
   {
      return securityEnabled;
   }
  
   public static void setSecurityEnabled(boolean enabled)
   {
      securityEnabled = enabled;
   }

   public static Identity instance()
   {
      if ( !Contexts.isSessionContextActive() )
      {
         throw new IllegalStateException("No active session context");
      }

      Identity instance = (Identity) Component.getInstance(Identity.class, ScopeType.SESSION);

      if (instance == null)
      {
         throw new IllegalStateException("No Identity could be created");
      }

      return instance;
   }
  
   public boolean isLoggedIn()
   {          
      return isLoggedIn(true);
   }
  
   public boolean isLoggedIn(boolean attemptLogin)
   {
      if (!authenticating && attemptLogin && getPrincipal() == null && isCredentialsSet() &&
          Contexts.isEventContextActive() &&
          !Contexts.getEventContext().isSet(LOGIN_TRIED))
      {
         Contexts.getEventContext().set(LOGIN_TRIED, true);
         quietLogin();
      }    
     
      // If there is a principal set, then the user is logged in.
      return getPrincipal() != null;
   }

   public Principal getPrincipal()
   {
      return principal;
   }
  
   public Subject getSubject()
   {
      return subject;
   }
  
   public boolean isCredentialsSet()
   {
      return username != null && password != null;
   }
     
   /**
    * Performs an authorization check, based on the specified security expression.
    *
    * @param expr The security expression to evaluate
    * @throws NotLoggedInException Thrown if the authorization check fails and
    * the user is not authenticated
    * @throws AuthorizationException Thrown if the authorization check fails and
    * the user is authenticated
    */
   public void checkRestriction(String expr)
   {     
      if (!securityEnabled) return;
     
      if ( !evaluateExpression(expr) )
      {
         if ( !isLoggedIn() )
         {
//          TODO - Deprecated, remove for next major release
            if (Events.exists()) Events.instance().raiseEvent("org.jboss.seam.notLoggedIn");           
           
            if (Events.exists()) Events.instance().raiseEvent(EVENT_NOT_LOGGED_IN);
            log.debug(String.format(
               "Error evaluating expression [%s] - User not logged in", expr));
            throw new NotLoggedInException();
         }
         else
         {
            if (Events.exists()) Events.instance().raiseEvent(EVENT_NOT_AUTHORIZED);
            throw new AuthorizationException(String.format(
               "Authorization check failed for expression [%s]", expr));
         }
      }
   }

   public String login()
   {
      try
      {
         authenticate();
         if ( log.isDebugEnabled() )
         {
            log.debug("Login successful for: " + getUsername());
         }

//       TODO - Deprecated, remove for next major release
         if (Events.exists()) Events.instance().raiseEvent("org.jboss.seam.loginSuccessful");
        
         if (Events.exists()) Events.instance().raiseEvent(EVENT_LOGIN_SUCCESSFUL);
         return "loggedIn";
      }
      catch (LoginException ex)
      {
         if ( log.isDebugEnabled() )
         {
             log.debug("Login failed for: " + getUsername(), ex);
         }
         if (Events.exists()) Events.instance().raiseEvent(EVENT_LOGIN_FAILED, ex);
         return null;
      }
   }
  
   /**
    * Attempts a quiet login, suppressing any login exceptions and not creating
    * any faces messages. This method is intended to be used primarily as an
    * internal API call, however has been made public for convenience.
    */
   public void quietLogin()
   {
      try
      {
         if (isCredentialsSet()) authenticate();
      }
      catch (LoginException ex) { }
   }
  
   public void authenticate()
      throws LoginException
   {
      // If we're already authenticated, then don't authenticate again
      if (!isLoggedIn(false))
      {
         authenticate( getLoginContext() );
      }
   }

   public void authenticate(LoginContext loginContext)
      throws LoginException
   {
      try
      {
         authenticating = true;
         preAuthenticate();
         loginContext.login();
         postAuthenticate();        
      }
      finally
      {
         authenticating = false;
      }
   }
  
   protected void preAuthenticate()
   {
      unAuthenticate();
      preAuthenticationRoles.clear();
     
      // TODO - Deprecated, remove for next major release
      if (Events.exists()) Events.instance().raiseEvent("org.jboss.seam.preAuthenticate");     
     
      if (Events.exists()) Events.instance().raiseEvent(EVENT_PRE_AUTHENTICATE);
   }  
  
   protected void postAuthenticate()
   {
      // Populate the working memory with the user's principals
      for ( Principal p : getSubject().getPrincipals() )
      {        
         if ( !(p instanceof Group))
         {
            if (principal == null)
            {
               principal = p;
               break;
            }           
         }        
      }     
     
      if (!preAuthenticationRoles.isEmpty() && isLoggedIn())
      {
         for (String role : preAuthenticationRoles)
         {
            addRole(role);
         }
         preAuthenticationRoles.clear();
      }
     
      password = null;

      // TODO - Deprecated, remove for next major release
      if (Events.exists()) Events.instance().raiseEvent("org.jboss.seam.postAuthenticate");
     
      if (Events.exists()) Events.instance().raiseEvent(EVENT_POST_AUTHENTICATE, this);
   }
  
   /**
    * Removes all Role objects from the security context, removes the "Roles"
    * group from the user's subject.
    *
    */
   protected void unAuthenticate()
   {     
      principal = null;
     
      for ( Group sg : getSubject().getPrincipals(Group.class) )     
      {
         if ( ROLES_GROUP.equals( sg.getName() ) )
         {
            getSubject().getPrincipals().remove(sg);
            break;
         }
      }
   }

   protected LoginContext getLoginContext() throws LoginException
   {
      if (getJaasConfigName() != null)
      {
         return new LoginContext(getJaasConfigName(), getSubject(),
                  getDefaultCallbackHandler());
      }
     
      return new LoginContext(Configuration.DEFAULT_JAAS_CONFIG_NAME,
               getSubject(), getDefaultCallbackHandler(), Configuration.instance());
   }
  
   public void logout()
   {
      principal = null;
      unAuthenticate();
      Session.instance().invalidate();
      if (Events.exists()) Events.instance().raiseEvent(EVENT_LOGGED_OUT);
     
      // TODO - Deprecated, remove for next major release
      if (Events.exists()) Events.instance().raiseEvent("org.jboss.seam.loggedOut");     
   }

   /**
    * Checks if the authenticated Identity is a member of the specified role.
    *
    * @param role String The name of the role to check
    * @return boolean True if the user is a member of the specified role
    */
   public boolean hasRole(String role)
   {
      if (!securityEnabled) return true;
     
      isLoggedIn(true);
     
      for ( Group sg : getSubject().getPrincipals(Group.class) )     
      {
         if ( ROLES_GROUP.equals( sg.getName() ) )
         {
            return sg.isMember( new SimplePrincipal(role) );
         }
      }
      return false;
   }
  
   /**
    * Adds a role to the user's subject, and their security context
    *
    * @param role The name of the role to add
    */
   public boolean addRole(String role)
   {
      if (role == null || "".equals(role)) return false;
     
      if (!isLoggedIn())
      {
         preAuthenticationRoles.add(role);
         return false;
      }
      else
      {
         for ( Group sg : getSubject().getPrincipals(Group.class) )     
         {
            if ( ROLES_GROUP.equals( sg.getName() ) )
            {
               return sg.addMember(new SimplePrincipal(role));
            }
         }
                 
         SimpleGroup roleGroup = new SimpleGroup(ROLES_GROUP);
         roleGroup.addMember(new SimplePrincipal(role));
         getSubject().getPrincipals().add(roleGroup);
         return true;
      }
   }

   /**
    * Removes a role from the user's subject and their security context
    *
    * @param role The name of the role to remove
    */
   public void removeRole(String role)
   {    
      for ( Group sg : getSubject().getPrincipals(Group.class) )     
      {
         if ( ROLES_GROUP.equals( sg.getName() ) )
         {
            Enumeration e = sg.members();
            while (e.hasMoreElements())
            {
               Principal member = (Principal) e.nextElement();
               if (member.getName().equals(role))
               {
                  sg.removeMember(member);
                  break;
               }
            }

         }
      }     
   }  
  
   /**
    * Assert that the current authenticated Identity is a member of
    * the specified role.
    *
    * @param role String The name of the role to check
    * @throws AuthorizationException if not a member
    */
   public void checkRole(String role)
   {
      isLoggedIn(true);
     
      if ( !hasRole(role) )
      {
         if ( !isLoggedIn() )
         {
            // TODO - Deprecated, remove for next major release
            if (Events.exists()) Events.instance().raiseEvent("org.jboss.seam.notLoggedIn");
           
            if (Events.exists()) Events.instance().raiseEvent(EVENT_NOT_LOGGED_IN);
            throw new NotLoggedInException();
         }
         else
         {
            throw new AuthorizationException(String.format(
                  "Authorization check failed for role [%s]", role));
         }
      }
   }

   /**
    * Assert that the current authenticated Identity has permission for
    * the specified name and action
    *
    * @param name String The permission name
    * @param action String The permission action
    * @param arg Object Optional object parameter used to make a permission decision
    * @throws AuthorizationException if the user does not have the specified permission
    */
   public void checkPermission(String name, String action, Object...arg)
   {
      isLoggedIn(true);
     
      if ( !hasPermission(name, action, arg) )
      {
         if ( !isLoggedIn() )
         {
//          TODO - Deprecated, remove for next major release
            if (Events.exists()) Events.instance().raiseEvent("org.jboss.seam.notLoggedIn");           
           
            if (Events.exists()) Events.instance().raiseEvent(EVENT_NOT_LOGGED_IN);
            throw new NotLoggedInException();
         }
         else
         {
            throw new AuthorizationException(String.format(
                  "Authorization check failed for permission [%s,%s]", name, action));
         }
      }
   }

   /**
    * Performs a permission check for the specified name and action
    *
    * @param name String The permission name
    * @param action String The permission action
    * @param arg Object Optional object parameter used to make a permission decision
    * @return boolean True if the user has the specified permission
    */
   public boolean hasPermission(String name, String action, Object...arg)
   {     
      return !securityEnabled;
   }  
  
   /**
    * Creates a callback handler that can handle a standard username/password
    * callback, using the username and password properties.
    */
   protected CallbackHandler getDefaultCallbackHandler()
   {
      return new CallbackHandler()
      {
         public void handle(Callback[] callbacks)
            throws IOException, UnsupportedCallbackException
         {
            for (int i=0; i<callbacks.length; i++)
            {
               if (callbacks[i] instanceof NameCallback)
               {
                  ( (NameCallback) callbacks[i] ).setName(getUsername());
               }
               else if (callbacks[i] instanceof PasswordCallback)
               {
                  ( (PasswordCallback) callbacks[i] ).setPassword( getPassword() != null ?
                           getPassword().toCharArray() : null );
               }
               else
               {
                  throw new UnsupportedCallbackException(callbacks[i], "Unsupported callback");
               }
            }
           
         }
      };
   }
  
   /**
    * Evaluates the specified security expression, which must return a boolean
    * value.
    *
    * @param expr String The expression to evaluate
    * @return boolean The result of the expression evaluation
    */
   protected boolean evaluateExpression(String expr)
   {   
      // The following line doesn't work in MyFaces     
      return Expressions.instance().createValueExpression(expr, Boolean.class).getValue();
   }  
  
   public String getUsername()
   {
      return username;
   }
  
   public void setUsername(String username)
   { 
      if (this.username != username && (this.username == null || !this.username.equals(username)))
      {
         this.username = username;
         if (Events.exists()) Events.instance().raiseEvent(EVENT_CREDENTIALS_UPDATED);
      }
   }
  
   public String getPassword()
   {
      return password;
   }
  
   public void setPassword(String password)
   {
      if (this.password != password && (this.password == null || !this.password.equals(password)))
      {
         this.password = password;
         if (Events.exists()) Events.instance().raiseEvent(EVENT_CREDENTIALS_UPDATED);
      }     
   }
  
   public MethodExpression getAuthenticateMethod()
   {
      return authenticateMethod;
   }
  
   public void setAuthenticateMethod(MethodExpression authMethod)
   {
      this.authenticateMethod = authMethod;
   }
  
   public boolean isRememberMe()
   {
      return rememberMe;
   }
  
   public void setRememberMe(boolean remember)
   {
      if (this.rememberMe != remember)
      {
         this.rememberMe = remember;
         if (Events.exists()) Events.instance().raiseEvent(EVENT_REMEMBER_ME, this);
      }
   }
  
   public String getJaasConfigName()
   {
      return jaasConfigName;
   }
  
   public void setJaasConfigName(String jaasConfigName)
   {
      this.jaasConfigName = jaasConfigName;
   }
  
   synchronized void runAs(RunAsOperation operation)
   {
      Principal savedPrincipal = getPrincipal();
      Subject savedSubject = getSubject();
     
      try
      {
         principal = operation.getPrincipal();
         subject = operation.getSubject();
        
         operation.execute();
      }
      finally
      {
         principal = savedPrincipal;
         subject = savedSubject;
      }
   }

   public void checkEntityPermission(Object entity, EntityAction action)
   {     
      isLoggedIn(true);
     
      PersistenceProvider provider = PersistenceProvider.instance();
      Class beanClass = provider.getBeanClass(entity);
     
      if (beanClass != null)
      {
         String name = Seam.getComponentName(entity.getClass());
         if (name == null) name = beanClass.getName()
        
         Method m = null;
         switch (action)
         {
            case READ:
               m = provider.getPostLoadMethod(beanClass);
               break;
            case INSERT:
               m = provider.getPrePersistMethod(beanClass);
               break;
            case UPDATE:
               m = provider.getPreUpdateMethod(beanClass);
               break;
            case DELETE:
               m = provider.getPreRemoveMethod(beanClass);
         }
        
         Restrict restrict = null;
        
         if (m != null && m.isAnnotationPresent(Restrict.class))
         {
            restrict = m.getAnnotation(Restrict.class);
         }
         else if (entity.getClass().isAnnotationPresent(Restrict.class))
         {
            restrict = entity.getClass().getAnnotation(Restrict.class);
         }

         if (restrict != null)
         {
            if (Strings.isEmpty(restrict.value()))
            {
               checkPermission(name, action.toString(), entity);
            }
            else
            {
               checkRestriction(restrict.value());
            }
         }
      }
   }  
}
TOP

Related Classes of org.jboss.seam.security.Identity

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.