Package org.beangle.security.access.intercept

Source Code of org.beangle.security.access.intercept.AbstractSecurityInterceptor

/* Copyright c 2005-2012.
* Licensed under GNU  LESSER General Public License, Version 3.
* http://www.gnu.org/licenses
*/
package org.beangle.security.access.intercept;

import org.apache.commons.lang.Validate;
import org.beangle.security.access.AccessDeniedException;
import org.beangle.security.access.AuthorityManager;
import org.beangle.security.auth.AuthenticationManager;
import org.beangle.security.core.Authentication;
import org.beangle.security.core.AuthenticationException;
import org.beangle.security.core.context.SecurityContextHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

/**
* Abstract class that implements security interception for secure objects.
* <p>
* The <code>AbstractSecurityInterceptor</code> will ensure the proper startup configuration of the
* security interceptor. It will also implement the proper handling of secure object invocations,
* namely:
* <ol>
* <li>Obtain the {@link Authentication} object from the {@link SecurityContextHolder}.</li>
* <li>Determine if the request relates to a secured or public invocation by looking up the secure
* object request against the {@link ObjectDefinitionSource}.</li>
* <li>For an invocation that is secured (there is a <code>ConfigAttributeDefinition</code> for the
* secure object invocation):
* <ol type="a">
* <li>If either the {@link org.beangle.security.core.Authentication#isAuthenticated()} returns
* <code>false</code>, or the {@link #alwaysReauthenticate} is <code>true</code> , authenticate the
* request against the configured {@link AuthenticationManager}. When authenticated, replace the
* <code>Authentication</code> object on the <code>SecurityContextHolder</code> with the returned
* value.</li>
* <li>Authorize the request against the configured {@link AuthorityManager}.</li>
* <li>Perform any run-as replacement via the configured {@link RunAsManager}.</li>
* <li>Pass control back to the concrete subclass, which will actually proceed with executing the
* object. A {@link InterceptorStatusToken} is returned so that after the subclass has finished
* proceeding with execution of the object, its finally clause can ensure the
* <code>AbstractSecurityInterceptor</code> is re-called and tidies up correctly.</li>
* <li>The concrete subclass will re-call the <code>AbstractSecurityInterceptor</code> via the
* {@link #afterInvocation(InterceptorStatusToken, Object)} method.</li>
* <li>If the <code>RunAsManager</code> replaced the <code>Authentication</code> object, return the
* <code>SecurityContextHolder</code> to the object that existed after the call to
* <code>AuthenticationManager</code>.</li>
* <li>If an <code>AfterInvocationManager</code> is defined, invoke the invocation manager and allow
* it to replace the object due to be returned to the caller.</li>
* </ol>
* </li>
* <li>For an invocation that is public (there is no <code>ConfigAttributeDefinition</code> for the
* secure object invocation):
* <ol type="a">
* <li>As described above, the concrete subclass will be returned an
* <code>InterceptorStatusToken</code> which is subsequently re-presented to the
* <code>AbstractSecurityInterceptor</code> after the secure object has been executed. The
* <code>AbstractSecurityInterceptor</code> will take no further action when its
* {@link #afterInvocation(InterceptorStatusToken, Object)} is called.</li>
* </ol>
* </li>
* <li>Control again returns to the concrete subclass, along with the <code>Object</code> that
* should be returned to the caller. The subclass will then return that result or exception to the
* original caller.</li>
* </ol>
*/
public abstract class AbstractSecurityInterceptor implements InitializingBean {

  protected static final Logger logger = LoggerFactory.getLogger(AbstractSecurityInterceptor.class);

  private AuthorityManager authorityManager;
  private AuthenticationManager authenticationManager;

  private boolean alwaysReauthenticate = false;
  private boolean rejectPublicInvocations = false;
  private boolean validateConfigAttributes = true;

  /**
   * Completes the work of the <tt>AbstractSecurityInterceptor</tt> after the
   * secure object invocation has been completed.
   *
   * @param token
   *            as returned by the {@link #beforeInvocation(Object)} method
   * @param returnedObject
   *            any object returned from the secure object invocation (may be <tt>null</tt>)
   * @return the object the secure object invocation should ultimately return
   *         to its caller (may be <tt>null</tt>)
   */
  protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
    if (token == null) {
      // public object
      return returnedObject;
    }
    if (token.isContextHolderRefreshRequired()) {
      if (logger.isDebugEnabled()) {
        logger.debug("Reverting to original Authentication: " + token.getAuthentication().toString());
      }

      SecurityContextHolder.getContext().setAuthentication(token.getAuthentication());
    }
    return returnedObject;
  }

  public void afterPropertiesSet() throws Exception {
    Validate.notNull(getSecureObjectClass(),
        "Subclass must provide a non-null response to getSecureObjectClass()");
    Validate.notNull(this.authenticationManager, "An AuthenticationManager is required");
    Validate.notNull(this.authorityManager, "An AuthorityManager is required");
  }

  protected InterceptorStatusToken beforeInvocation(Object object) {
    Validate.notNull(object, "Object was null");
    if (!getSecureObjectClass().isAssignableFrom(object.getClass())) { throw new IllegalArgumentException(
        "Security invocation attempted for object "
            + object.getClass().getName()
            + " but AbstractSecurityInterceptor only configured to support secure objects of type: "
            + getSecureObjectClass()); }
    Authentication authenticated = authenticateIfRequired();

    // Attempt authorization
    if (!authorityManager.isAuthorized(authenticated, object)) { throw new AccessDeniedException(object,
        "access denied"); }
    logger.debug("Authorization successful");

    // AuthorizedEvent event = new AuthorizedEvent(object, attr,
    // authenticated);
    // publishEvent(event);

    // Attempt to run as a different user
    // Authentication runAs = this.runAsManager.buildRunAs(authenticated,
    // object, attr);

    return new InterceptorStatusToken(authenticated, false, object);

    // if (runAs == null) {
    // if (logger.isDebugEnabled()) {
    // logger.debug("RunAsManager did not change Authentication object");
    // }
    //
    // // no further work post-invocation
    // return new InterceptorStatusToken(authenticated, false, attr,
    // object);
    // } else {
    // if (logger.isDebugEnabled()) {
    // logger.debug("Switching to RunAs Authentication: " + runAs);
    // }
    // SecurityContextHolder.getContext().setAuthentication(runAs);
    // // revert to token.Authenticated post-invocation
    // return new InterceptorStatusToken(authenticated, true, attr, object);
    // }
  }

  /**
   * Checks the current authentication token and passes it to the
   * AuthenticationManager if {@link org.beangle.security.core.Authentication#isAuthenticated()}
   * returns false or the property <tt>alwaysReauthenticate</tt> has been set
   * to true.
   *
   * @return an authenticated <tt>Authentication</tt> object.
   */
  private Authentication authenticateIfRequired() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if(null==authentication){
      throw new AuthenticationException();
    }
    if (authentication.isAuthenticated() && !alwaysReauthenticate) {
      logger.debug("Previously Authenticated: {}", authentication);
      return authentication;
    }
    authentication = authenticationManager.authenticate(authentication);
    SecurityContextHolder.getContext().setAuthentication(authentication);
    return authentication;
  }

  public AuthorityManager getAuthorityManager() {
    return authorityManager;
  }

  public AuthenticationManager getAuthenticationManager() {
    return this.authenticationManager;
  }

  /**
   * Indicates the type of secure objects the subclass will be presenting to
   * the abstract parent for processing. This is used to ensure collaborators
   * wired to the <code>AbstractSecurityInterceptor</code> all support the
   * indicated secure object class.
   *
   * @return the type of secure object the subclass provides services for
   */
  public abstract Class<?> getSecureObjectClass();

  public boolean isAlwaysReauthenticate() {
    return alwaysReauthenticate;
  }

  public boolean isRejectPublicInvocations() {
    return rejectPublicInvocations;
  }

  public boolean isValidateConfigAttributes() {
    return validateConfigAttributes;
  }

  public void setAuthorityManager(AuthorityManager authorityManager) {
    this.authorityManager = authorityManager;
  }

  /**
   * Indicates whether the <code>AbstractSecurityInterceptor</code> should
   * ignore the {@link Authentication#isAuthenticated()} property. Defaults to <code>false</code>,
   * meaning by default the <code>Authentication.isAuthenticated()</code> property is trusted and
   * re-authentication will not occur if the principal has already been
   * authenticated.
   *
   * @param alwaysReauthenticate
   *            <code>true</code> to force <code>AbstractSecurityInterceptor</code> to disregard
   *            the
   *            value of <code>Authentication.isAuthenticated()</code> and
   *            always re-authenticate the request (defaults to <code>false</code>).
   */
  public void setAlwaysReauthenticate(boolean alwaysReauthenticate) {
    this.alwaysReauthenticate = alwaysReauthenticate;
  }

  public void setAuthenticationManager(AuthenticationManager newManager) {
    this.authenticationManager = newManager;
  }

  /**
   * By rejecting public invocations (and setting this property to <tt>true</tt>), essentially you
   * are ensuring that every secure object
   * invocation advised by <code>AbstractSecurityInterceptor</code> has a
   * configuration attribute defined. This is useful to ensure a "fail safe"
   * mode where undeclared secure objects will be rejected and configuration
   * omissions detected early. An <tt>IllegalArgumentException</tt> will be
   * thrown by the <tt>AbstractSecurityInterceptor</tt> if you set this
   * property to <tt>true</tt> and an attempt is made to invoke a secure
   * object that has no configuration attributes.
   *
   * @param rejectPublicInvocations
   *            set to <code>true</code> to reject invocations of secure
   *            objects that have no configuration attributes (by default it
   *            is <code>false</code> which treats undeclared secure objects
   *            as "public" or unauthorized).
   */
  public void setRejectPublicInvocations(boolean rejectPublicInvocations) {
    this.rejectPublicInvocations = rejectPublicInvocations;
  }

  public void setValidateConfigAttributes(boolean validateConfigAttributes) {
    this.validateConfigAttributes = validateConfigAttributes;
  }
}
TOP

Related Classes of org.beangle.security.access.intercept.AbstractSecurityInterceptor

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.