/* * Databinder: a simple bridge from Wicket to Hibernate * Copyright (C) 2008 Nathan Hamblen nathan@technically.us * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package net.databinder.auth; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import javax.servlet.http.Cookie; import net.databinder.CookieRequestCycle; import net.databinder.auth.data.DataUser; import org.apache.wicket.Application; import org.apache.wicket.Request; import org.apache.wicket.RequestCycle; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.model.IModel; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.protocol.http.WebResponse; import org.apache.wicket.protocol.http.WebSession; import org.apache.wicket.util.time.Duration; /** * Base class for Databinder implementations providing an implementation for * authentication cookies and current user lookup. */ public abstract class AuthDataSessionBase<T extends DataUser> extends WebSession implements AuthSession<T> { /** Effective signed in state. */ private IModel<T> userModel; private static final String CHARACTER_ENCODING = "UTF-8"; /** * Initialize new session. * @see WebApplication */ public AuthDataSessionBase(Request request) { super(request); } protected AuthApplication<T> getApp() { return (AuthApplication<T>) Application.get(); } public static AuthDataSessionBase get() { return (AuthDataSessionBase) WebSession.get(); } /** * @return DataUser object for current user, or null if none signed in. */ public T getUser() { if (isSignedIn()) { return getUserModel().getObject(); } return null; } public IModel<T> getUserModel() { return userModel; } /** * @return model for current user */ public abstract IModel<T> createUserModel(T user); /** * @return length of time sign-in cookie should persist, defined here as one month */ protected Duration getSignInCookieMaxAge() { return Duration.days(31); } /** * Determine if user is signed in, or can be via cookie. * @return true if signed in or cookie sign in is possible and successful */ public boolean isSignedIn() { if (userModel == null) cookieSignIn(); return userModel != null; } /** * @return true if signed in, false if credentials incorrect */ public boolean signIn(String username, String password) { return signIn(username, password, false); } /** * @param setCookie if true, sets cookie to remember user * @return true if signed in, false if credentials incorrect */ public boolean signIn(final String username, final String password, boolean setCookie) { clearUser(); T potential = getUser(username); if (potential != null && (potential).getPassword().matches(password)) signIn(potential, setCookie); return userModel != null; } /** * Sign in a user whose credentials have been validated elsewhere. The user object must exist, * and already have been saved, in the current request's Hibernate session. * @param user validated and persisted user, must be in current Hibernate session * @param setCookie if true, sets cookie to remember user */ public void signIn(T user, boolean setCookie) { userModel = createUserModel(user); if (setCookie) setCookie(); } /** * Attempts cookie sign in, which will set usename field but not user. * @return true if signed in, false if credentials incorrect or unavailable */ protected boolean cookieSignIn() { CookieRequestCycle requestCycle = (CookieRequestCycle) RequestCycle.get(); Cookie userCookie = requestCycle.getCookie(getUserCookieName()), token = requestCycle.getCookie(getAuthCookieName()); if (userCookie != null && token != null) { T potential; try { potential = getUser(URLDecoder.decode(userCookie.getValue(), CHARACTER_ENCODING)); } catch (UnsupportedEncodingException e) { throw new WicketRuntimeException(e); } if (potential != null && potential instanceof DataUser) { String correctToken = getApp().getToken(potential); if (correctToken.equals(token.getValue())) signIn(potential, false); } } return userModel != null; } /** * Looks for a persisted DataUser object matching the given username. Uses the user class * and criteria builder returned from the application subclass implementing AuthApplication. * @param username * @return user object from persistent storage * @see AuthApplication */ protected T getUser(final String username) { return getApp().getUser(username); } public static String getUserCookieName() { return Application.get().getClass().getSimpleName() + "_USER"; } public static String getAuthCookieName() { return Application.get().getClass().getSimpleName() + "_AUTH"; } /** * Sets cookie to remember the currently signed-in user. Sets max age to * value from getSignInCookieMaxAge(). * @see AuthDataSessionBase#getSignInCookieMaxAge() */ protected void setCookie() { if (userModel == null) throw new WicketRuntimeException("User must be signed in when calling this method"); T cookieUser = getUser(); WebResponse resp = (WebResponse) RequestCycle.get().getResponse(); Cookie name, auth; try { name = new Cookie(getUserCookieName(), URLEncoder.encode(cookieUser.getUsername(), CHARACTER_ENCODING)); auth = new Cookie(getAuthCookieName(), getApp().getToken(cookieUser)); } catch (UnsupportedEncodingException e) { throw new WicketRuntimeException(e); } int maxAge = (int) getSignInCookieMaxAge().seconds(); name.setMaxAge(maxAge); auth.setMaxAge(maxAge); RequestCycle rc = RequestCycle.get(); if (rc instanceof CookieRequestCycle) { CookieRequestCycle cookieRc = (CookieRequestCycle) rc; cookieRc.applyScope(name); cookieRc.applyScope(auth); } resp.addCookie(name); resp.addCookie(auth); } /** * Detach userModel manually, as it isnt' attached to any component. */ @Override protected void detach() { if (userModel != null) userModel.detach(); } /** Nullifies userModela nd clears authentication cookies. */ protected void clearUser() { userModel = null; CookieRequestCycle requestCycle = (CookieRequestCycle) RequestCycle.get(); requestCycle.clearCookie(getUserCookieName()); requestCycle.clearCookie(getAuthCookieName()); } /** Signs out and invalidates session. */ public void signOut() { clearUser(); getSessionStore().invalidate(RequestCycle.get().getRequest()); } }