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 }