001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.shiro.spring.remoting;
020    
021    import org.apache.shiro.SecurityUtils;
022    import org.apache.shiro.mgt.SecurityManager;
023    import org.apache.shiro.subject.ExecutionException;
024    import org.apache.shiro.subject.Subject;
025    import org.slf4j.Logger;
026    import org.slf4j.LoggerFactory;
027    import org.springframework.remoting.support.DefaultRemoteInvocationExecutor;
028    import org.springframework.remoting.support.RemoteInvocation;
029    
030    import java.io.Serializable;
031    import java.lang.reflect.InvocationTargetException;
032    import java.util.concurrent.Callable;
033    
034    
035    /**
036     * An implementation of the Spring {@link org.springframework.remoting.support.RemoteInvocationExecutor}
037     * that binds a {@code sessionId} to the incoming thread to make it available to the {@code SecurityManager}
038     * implementation during the thread execution.  The {@code SecurityManager} implementation can use this sessionId
039     * to reconstitute the {@code Subject} instance based on persistent state in the corresponding {@code Session}.
040     *
041     * @author Jeremy Haile
042     * @author Les Hazlewood
043     * @since 0.1
044     */
045    public class SecureRemoteInvocationExecutor extends DefaultRemoteInvocationExecutor {
046    
047        //TODO - complete JavaDoc
048    
049        /*--------------------------------------------
050        |             C O N S T A N T S             |
051        ============================================*/
052    
053        /*--------------------------------------------
054        |    I N S T A N C E   V A R I A B L E S    |
055        ============================================*/
056        private static final Logger log = LoggerFactory.getLogger(SecureRemoteInvocationExecutor.class);
057    
058        /**
059         * The SecurityManager used to retrieve realms that should be associated with the
060         * created <tt>Subject</tt>s upon remote invocation.
061         */
062        private SecurityManager securityManager;
063    
064        /*--------------------------------------------
065        |         C O N S T R U C T O R S           |
066        ============================================*/
067    
068        /*--------------------------------------------
069        |  A C C E S S O R S / M O D I F I E R S    |
070        ============================================*/
071    
072        public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) {
073            this.securityManager = securityManager;
074        }
075    
076        /*--------------------------------------------
077        |               M E T H O D S               |
078        ============================================*/
079        @SuppressWarnings({"unchecked"})
080        public Object invoke(final RemoteInvocation invocation, final Object targetObject)
081                throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
082    
083            try {
084                SecurityManager securityManager =
085                        this.securityManager != null ? this.securityManager : SecurityUtils.getSecurityManager();
086    
087                Subject.Builder builder = new Subject.Builder(securityManager);
088    
089                String host = (String) invocation.getAttribute(SecureRemoteInvocationFactory.HOST_KEY);
090                if (host != null) {
091                    builder.host(host);
092                }
093    
094                Serializable sessionId = invocation.getAttribute(SecureRemoteInvocationFactory.SESSION_ID_KEY);
095                if (sessionId != null) {
096                    builder.sessionId(sessionId);
097                } else {
098                    if (log.isTraceEnabled()) {
099                        log.trace("RemoteInvocation did not contain a Shiro Session id attribute under " +
100                                "key [" + SecureRemoteInvocationFactory.SESSION_ID_KEY + "].  A Subject based " +
101                                "on an existing Session will not be available during the method invocatin.");
102                    }
103                }
104    
105                Subject subject = builder.buildSubject();
106                return subject.execute(new Callable() {
107                    public Object call() throws Exception {
108                        return SecureRemoteInvocationExecutor.super.invoke(invocation, targetObject);
109                    }
110                });
111            } catch (ExecutionException e) {
112                Throwable cause = e.getCause();
113                if (cause instanceof NoSuchMethodException) {
114                    throw (NoSuchMethodException) cause;
115                } else if (cause instanceof IllegalAccessException) {
116                    throw (IllegalAccessException) cause;
117                } else if (cause instanceof InvocationTargetException) {
118                    throw (InvocationTargetException) cause;
119                } else {
120                    throw new InvocationTargetException(cause);
121                }
122            } catch (Throwable t) {
123                throw new InvocationTargetException(t);
124            }
125        }
126    }