guys.
I have the following code for my http session at Wicket-based application:
public static HttpServletRequest getHttpServletRequest() {
Request request = RequestCycle.get().getRequest();
if (request != null && request instanceof WebRequest) return
HttpServletRequest) request.getContainerRequest();
return null;
}
public static SessionObject getSessionObject() {
HttpServletRequest request = getHttpServletRequest();
HttpSession session = request == null ? null : request.getSession();
SessionObject so = session == null ? null : (SessionObject) session.getAttribute("so");
if (so == null) {
logger.warn("SessionObject is not found in HttpSession!");
}
return so;
}
The session object is initialized at jsp like the following:
jsp:useBean id="so" class="package.SessionObject" scope="session"
I'd like to mock this attribute so into Wicket tests.
Tried to do the following:
bind(SessionObject.class).toInstance(EasyMock.createMock(SessionObject.class));
also
tester = new WicketTester(new MockApplication() {
#Override
public Session newSession(Request request, Response response) {
final Session session = super.newSession(request, response);
session.setAttribute("so", EasyMock.createMock(SessionObject.class));
return session;
}
});
But when I try to call method as:
init(){
a = getSessionObject().getA();
}
getSessionObject() returns null because there are no attribute named "so".
Could you help please me to mock this attribute into session?
You can simplify your helper methods to: Session.get().getAttribute("so").
Your code that writes the value already uses Session#setAttribute().
Try by binding the session: Session#bind(). Unless bound Wicket will create a new instance of Session for each request. Once bound Wicket will acquire HttpSession and store Wicket's Session into it (as attribute).
If this doesn't help then put a breakpoint at Session set/getAttribute() methods and see what happens.
Related
I have an application with #CustomFormAuthenticationMechanismDefinition, and I would like to log the username, session id, IP address, etc. both at login and at logout. The HttpAuthMechanism that gets applied with this annotation associate the given session with the principal, which I can access through the SecurityContext. With a direct logout, I have no problem logging, but I would also like to log when session times out. So I created a HttpSessionListener and in its sessionDestroyed() method I try to access the logged in user through SecurityContext, but it returns an empty set, maybe because the securityContext got invalidated already.
One solution I have in my mind is to store the user principal in a session parameter (which likely happens with the HttpAuthMechanism implementation) and access it from there from the HttpSessionEvent object, but that doesn't feel like the cleanest solution. Is there another Listener I can use or some other solution?
I went with the custom HttpAuthenticationMechanism, here is it if anyone would need it (though I would be more than glad to have some feedback on whether or not it has any security flaws, or improvements).
In an #ApplicationScoped class implementing HttpAuthenticationMechanism:
#Override
public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthenticationException {
if (!httpMessageContext.isProtected()) {
return httpMessageContext.doNothing();
}
HttpSession session = request.getSession(false);
Credential credential = httpMessageContext.getAuthParameters().getCredential();
// If we already have a session, we get the user from it, unless it's a new login
if (session != null && !(credential instanceof UsernamePasswordCredential)) {
User user = (User) session.getAttribute("user");
if (user != null) {
return httpMessageContext.notifyContainerAboutLogin(user, user.getRoles());
}
}
// If we either don't have a session or it has no user attribute, we redirect/forward to login page
if (!(credential instanceof UsernamePasswordCredential)) {
return redirect(request, response, httpMessageContext);
}
// Here we have a Credential, so we validate it with the registered IdentityStoreHandler (injected as idStoreHandler)
CredentialValidationResult validate = idStoreHandler.validate(credential);
Context context = new Context();
context.setIp(request.getRemoteAddr());
if (validate.getStatus() == CredentialValidationResult.Status.VALID) {
session = request.getSession(true);
CallerPrincipal callerPrincipal = validate.getCallerPrincipal();
session.setAttribute("user", callerPrincipal);
context.setUser(callerPrincipal);
context.setSessionId(session.getId());
Logger log = new Logger(logger, "validateRequest", context);
log.debug("Logged in user: " + callerPrincipal.getName());
String redirectPage = "whatYouWant.xhtml";
redirect(request, response, httpMessageContext, redirectPage);
return httpMessageContext.notifyContainerAboutLogin(validate);
} else if (validate.getStatus() == CredentialValidationResult.Status.NOT_VALIDATED) {
return redirect(request, response, httpMessageContext);
} else {
// Logging
return httpMessageContext.responseUnauthorized();
}
}
And in an implemented HttpSessionListener:
#Override
public void sessionDestroyed(HttpSessionEvent se) {
User user = (User) se.getSession().getAttribute("user");
if (user != null) {
// logging
}
}
In this post on stackoverflow, it is established that HttpSession is not thread-safe. Specifically,
The Developer has the responsibility for thread-safe access to the attribute objects themselves. This will protect the attribute collection inside the HttpSession object from concurrent access, eliminating the opportunity for an application to cause that collection to become corrupted.
However, looking at the implementation of Catalina's CsrfPreventionFilter, I see no synchronization applied:
HttpSession session = req.getSession(false);
#SuppressWarnings("unchecked")
LruCache<String> nonceCache = (session == null) ? null
: (LruCache<String>) session.getAttribute(
Constants.CSRF_NONCE_SESSION_ATTR_NAME);
...
if (nonceCache == null) {
nonceCache = new LruCache<String>(nonceCacheSize);
if (session == null) {
session = req.getSession(true);
}
session.setAttribute(
Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
}
Could you please explain if this kind of access is safe, and why?
Update: In response to Andrew's answer below: Please check the following code. In function process() of ThreadDemo, while req is a local variable, it's not thread-safe, as it holds reference to the shared object session.
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Session session = new Session();
int len = 10;
ThreadDemo[] t = new ThreadDemo[len];
for (int i = 0; i < len; i++) {
t[i] = new ThreadDemo(session);
t[i].start();
}
}
}
class ThreadDemo extends Thread {
private final Session session;
ThreadDemo(Session session) {
this.session = session;
}
#Override
public void run() {
Request request = new HttpRequest(session);
process(request);
}
private void process(Request request) {
HttpRequest req = (HttpRequest) request;
Session session = req.getSession();
if (session.getAttribute("TEST") == null)
session.putAttribute("TEST", new Object());
else
session.clearAttributes();
System.out.println(session.getAttribute("TEST") == null);
}
}
class Session {
private final Map<String, Object> session;
Session() {
session = new HashMap<>();
}
Object getAttribute(String attr) {
return session.get(attr);
}
void putAttribute(String attr, Object o) {
session.put(attr, o);
}
void clearAttributes() {
session.clear();
}
}
class Request {
private final Session session;
Request(Session session) {
this.session = session;
}
Session getSession() {
return session;
}
}
class HttpRequest extends Request {
HttpRequest(Session session) {
super(session);
}
}
From the source
HttpSession session = req.getSession(false); // line 196
req is local so is thread safe, and if concurrent requests are received where the above session (unique to each thread) results in null, then it's reasonably safe to assume the concurrent requests represent different users (or at least unrelated requests from a single user).
Later, a new session is established (unique to to each thread):
session = req.getSession(true); // line 217
At this point there is no way for another concurrent request to be related to the above non-null session since the client has not yet received a response so the client does not yet know its session ID to send during a subsequent request.
So it's safe to create the session attribute for the just created session:
session.setAttribute(Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache); // line 219
Then a response is sent back to the client with the session ID. The next request includes the session ID so
session = req.getSession(true); // line 217
will not be null. And similarly, nonceCache will not be null so this whole block
if (nonceCache == null) {
nonceCache = new LruCache<String>(nonceCacheSize);
if (session == null) {
session = req.getSession(true);
}
session.setAttribute(
Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
}
will be skipped during subsequent requests related to the existing session.
Access to the elements of the cache are synchronized in the nested LruCache class lines 358 and 364.
Since struts 2's default I18nInterceptor fails when the system is distributed, I have implemented a cookie based solution. I added an interceptor that looks for the request_locale param and sets the cookie. And it also looks for the cookie. If either the request_locale param comes or the cookie is present, I set the new Locale. This interceptor is called after the struts's I18N interceptor. But after setting, I don't see the language changing. Below is the intercept method of my interceptor.
public String intercept(ActionInvocation invocation) throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
String localeStr = request.getParameter("request_locale");
if (localeStr != null) {
setLocaleInCookie(localeStr);
}
if (localeStr == null) {
localeStr = getLocaleFromCookie();
}
if (localeStr != null) {
ActionContext.getContext().setLocale(new Locale(localeStr));
}
return invocation.invoke();
}
I would like to know If I am doing is right. Is there any other solution or workaround for this?
I have a login method in a GWT RPC Servlet, which gets a user from the session context and therefore determins, if the user is logged in or not. I want to port this method to the request factory approach (to get a proxy entity instead of a DTO).
But where can i place it? I can't place it in the Entity because there i don't have the session context. Whats the right approach here?
My RPC method currently looks like this:
#Override
public UserDTO isLoggedIn() {
// TODO Auto-generated method stub
HttpSession session = getThreadLocalRequest().getSession();
String userName = (String)session.getAttribute("userName");
if(userName !=null){
return new UserDTO(userName);
}
return null;
}
RequestFactory also provides methods for accessing the request and servlet context
HttpSession session = com.google.web.bindery.requestfactory.server.RequestFactoryServlet.getThreadLocalRequest().getSession();
Documentation can be found here:
http://google-web-toolkit.googlecode.com/svn/javadoc/latest/com/google/web/bindery/requestfactory/server/RequestFactoryServlet.html#getThreadLocalRequest()
Setting response in session
#ResourceMapping("SomeValue")
public void getSites(ResourceRequest request, ResourceResponse response) {
try {
String _emailAddress = UserUtils.getEmailAddress(request);
//Service call here
// Need not call the service if the user is in session.
render(sitesXML, MediaType.TEXT_XML.toString(),response);
}catch{
...}
...
...
return MYVALUE;
What framework are you using??
If you are using a framework, surely you can have a reference of the session object within the getSites() method. Try getting a session object from your framework.
Your code may look like as below,
String sitesXML="";
if(sessionObject.get("sitesXmlKey") == null){
sitesXML = (String) sitesService.getSitesForUser(String.class,_emailAddress);
sessionObject.put("sitesXMLKey",sitesXML);
}else{
sitesXML = (String)sessionObject.get("sitesXmlKey");
}