/*
* RHQ Management Platform
* Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.enterprise.server.rest;
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.EJBContext;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;
import org.rhq.core.domain.auth.Subject;
import org.rhq.enterprise.server.auth.SessionManager;
import org.rhq.enterprise.server.auth.SubjectManagerLocal;
/**
* Interceptor to set the 'caller' variable of the AbstractRestBean from the princpial
* that was passed in from the WEB tier.
*
* For this to work, classes need to
* <ul>
* <li>extends AbstractRestBean</li>
* <li>Add this class as Interceptor like this : @Interceptors(SetCallerInterceptor.class) </li>
* </ul>
* @author Heiko W. Rupp
*/
public class SetCallerInterceptor {
@Resource
private EJBContext ejbContext;
@EJB
private SubjectManagerLocal subjectManager;
private SessionManager sessionManager = SessionManager.getInstance();
/**
* We need to take the Principal that was passed through the web-integration,
* get an RHQ Subject and set a session for it. When the call was made, we need
* to invalidate the session again.
* @param ctx InvocationContext from the EJB invocation chain
* @return result of the method call
* @throws Exception from method call or if no (valid) principal was provided
*/
@AroundInvoke
public Object setCaller(InvocationContext ctx) throws Exception {
Subject caller=null;
java.security.Principal p = ejbContext.getCallerPrincipal();
if (p!=null) {
caller = subjectManager.getSubjectByName(p.getName());
}
if (caller==null) {
throw new IllegalAccessException("No calling principal provided");
}
// Get Subject with a session
caller = sessionManager.put(caller);
// Provide it to the EJB
AbstractRestBean target = (AbstractRestBean) ctx.getTarget();
target.caller = caller;
// Call the EJBs
Object result = ctx.proceed();
// if result is StreamingOutput, we do not want to invalidate the session until it
// is finished writing the output; otherwise, any secure SLSB calls will fail. We
// instead wrap the result in an instance of SecureStreamingOutput which
// invalidates the session after the output has been written.
if (result instanceof StreamingOutput) {
return new SecureStreamingOutput((StreamingOutput) result, caller);
}
// Invalidate the session again.
sessionManager.invalidate(caller.getSessionId());
return result;
}
private class SecureStreamingOutput implements StreamingOutput {
private StreamingOutput delegate;
private Subject caller;
public SecureStreamingOutput(StreamingOutput delegate, Subject caller) {
this.delegate = delegate;
this.caller = caller;
}
@Override
public void write(OutputStream output) throws IOException, WebApplicationException {
delegate.write(output);
sessionManager.invalidate(caller.getSessionId());
}
}
}