001: /*
002: * Copyright 2002-2005 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package info.jtrac.wicket;
018:
019: import info.jtrac.Jtrac;
020: import info.jtrac.acegi.JtracCasProxyTicketValidator;
021: import info.jtrac.domain.Space;
022: import info.jtrac.domain.User;
023: import info.jtrac.util.WebUtils;
024: import info.jtrac.wicket.yui.TestPage;
025: import java.util.List;
026: import java.util.Locale;
027: import javax.servlet.ServletContext;
028: import javax.servlet.http.Cookie;
029: import org.acegisecurity.Authentication;
030: import org.acegisecurity.AuthenticationException;
031: import org.acegisecurity.AuthenticationManager;
032: import org.acegisecurity.context.SecurityContextHolder;
033: import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
034: import org.springframework.context.ApplicationContext;
035: import org.springframework.web.context.support.WebApplicationContextUtils;
036: import org.apache.wicket.Component;
037: import org.apache.wicket.Request;
038: import org.apache.wicket.RequestCycle;
039: import org.apache.wicket.Response;
040: import org.apache.wicket.RestartResponseAtInterceptPageException;
041: import org.apache.wicket.Session;
042: import org.apache.wicket.authorization.Action;
043: import org.apache.wicket.authorization.IAuthorizationStrategy;
044: import org.apache.wicket.markup.html.pages.RedirectPage;
045: import org.apache.wicket.protocol.http.WebApplication;
046: import org.apache.wicket.protocol.http.WebRequest;
047: import org.apache.wicket.request.target.coding.IndexedParamUrlCodingStrategy;
048: import org.apache.wicket.resource.loader.IStringResourceLoader;
049: import org.slf4j.Logger;
050: import org.slf4j.LoggerFactory;
051: import org.springframework.beans.factory.NoSuchBeanDefinitionException;
052:
053: /**
054: * main wicket application for jtrac
055: * holds singleton service layer instance pulled from spring
056: */
057: public class JtracApplication extends WebApplication {
058:
059: private final static Logger logger = LoggerFactory
060: .getLogger(JtracApplication.class);
061:
062: private Jtrac jtrac;
063: private ApplicationContext applicationContext;
064: private JtracCasProxyTicketValidator jtracCasProxyTicketValidator;
065:
066: public Jtrac getJtrac() {
067: return jtrac;
068: }
069:
070: public ApplicationContext getApplicationContext() {
071: return applicationContext;
072: }
073:
074: // used only by CasLoginPage
075: public String getCasLoginUrl() {
076: if (jtracCasProxyTicketValidator == null) {
077: return null;
078: }
079: return jtracCasProxyTicketValidator.getLoginUrl();
080: }
081:
082: // used only by logout link in HeaderPanel
083: public String getCasLogoutUrl() {
084: if (jtracCasProxyTicketValidator == null) {
085: return null;
086: }
087: return jtracCasProxyTicketValidator.getLogoutUrl();
088: }
089:
090: @Override
091: public void init() {
092:
093: super .init();
094:
095: // get hold of spring managed service layer (see BasePage, BasePanel etc for how it is used)
096: ServletContext sc = getServletContext();
097: applicationContext = WebApplicationContextUtils
098: .getWebApplicationContext(sc);
099: jtrac = (Jtrac) applicationContext.getBean("jtrac");
100:
101: // check if acegi-cas authentication is being used, get reference to object to be used
102: // by wicket authentication to redirect to right pages for login / logout
103: try {
104: jtracCasProxyTicketValidator = (JtracCasProxyTicketValidator) applicationContext
105: .getBean("casProxyTicketValidator");
106: logger
107: .info("casProxyTicketValidator retrieved from application context: "
108: + jtracCasProxyTicketValidator);
109: } catch (NoSuchBeanDefinitionException nsbde) {
110: logger
111: .info("casProxyTicketValidator not found in application context, CAS single-sign-on is not being used");
112: }
113:
114: // delegate wicket i18n support to spring i18n
115: getResourceSettings().addStringResourceLoader(
116: new IStringResourceLoader() {
117: public String loadStringResource(Class clazz,
118: String key, Locale locale, String style) {
119: try {
120: return applicationContext.getMessage(key,
121: null, locale);
122: } catch (Exception e) {
123: // have to return null so that wicket can try to resolve again
124: // e.g. without prefixing component id etc.
125: if (logger.isDebugEnabled()) {
126: logger.debug("i18n failed for key: '"
127: + key + "', Class: " + clazz
128: + ", Style: " + style
129: + ", Exception: " + e);
130: }
131: return null;
132: }
133: }
134:
135: public String loadStringResource(
136: Component component, String key) {
137: Class clazz = component == null ? null
138: : component.getClass();
139: Locale locale = component == null ? Session
140: .get().getLocale() : component
141: .getLocale();
142: return loadStringResource(clazz, key, locale,
143: null);
144: }
145: });
146:
147: getSecuritySettings().setAuthorizationStrategy(
148: new IAuthorizationStrategy() {
149: public boolean isActionAuthorized(Component c,
150: Action a) {
151: return true;
152: }
153:
154: public boolean isInstantiationAuthorized(Class clazz) {
155: if (BasePage.class.isAssignableFrom(clazz)) {
156: if (((JtracSession) Session.get())
157: .isAuthenticated()) {
158: return true;
159: }
160: if (jtracCasProxyTicketValidator != null) {
161: // attempt CAS authentication ==========================
162: logger
163: .debug("checking if context contains CAS authentication");
164: Authentication authentication = SecurityContextHolder
165: .getContext()
166: .getAuthentication();
167: if (authentication != null
168: && authentication
169: .isAuthenticated()) {
170: logger
171: .debug("security context contains CAS authentication, initializing session");
172: ((JtracSession) Session.get())
173: .setUser((User) authentication
174: .getPrincipal());
175: return true;
176: }
177: }
178: // attempt remember-me auto login ==========================
179: if (attemptRememberMeAutoLogin()) {
180: return true;
181: }
182: // attempt guest access if there are "public" spaces =======
183: List<Space> spaces = getJtrac()
184: .findSpacesWhereGuestAllowed();
185: if (spaces.size() > 0) {
186: logger
187: .debug(spaces.size()
188: + " public space(s) available, initializing guest user");
189: User guestUser = new User();
190: guestUser.setLoginName("guest");
191: guestUser.setName("Guest");
192: guestUser.addSpaceWithRole(null,
193: "ROLE_GUEST");
194: for (Space space : spaces) {
195: guestUser.addSpaceWithRole(space,
196: "ROLE_GUEST");
197: }
198: ((JtracSession) Session.get())
199: .setUser(guestUser);
200: // and proceed
201: return true;
202: }
203: // not authenticated, go to login page
204: logger
205: .debug("not authenticated, forcing login, page requested was "
206: + clazz.getName());
207: if (jtracCasProxyTicketValidator != null) {
208: String serviceUrl = jtracCasProxyTicketValidator
209: .getServiceProperties()
210: .getService();
211: String loginUrl = jtracCasProxyTicketValidator
212: .getLoginUrl();
213: logger
214: .debug("cas authentication: service URL: "
215: + serviceUrl);
216: String redirectUrl = loginUrl
217: + "?service=" + serviceUrl;
218: logger
219: .debug("attempting to redirect to: "
220: + redirectUrl);
221: throw new RestartResponseAtInterceptPageException(
222: new RedirectPage(redirectUrl));
223: } else {
224: throw new RestartResponseAtInterceptPageException(
225: LoginPage.class);
226: }
227: }
228: return true;
229: }
230: });
231:
232: // friendly urls for selected pages
233: if (jtracCasProxyTicketValidator != null) {
234: mountBookmarkablePage("/login", CasLoginPage.class);
235: } else {
236: mountBookmarkablePage("/login", LoginPage.class);
237: }
238: mountBookmarkablePage("/logout", LogoutPage.class);
239: mountBookmarkablePage("/svn", SvnStatsPage.class);
240: mountBookmarkablePage("/test", TestPage.class);
241: mountBookmarkablePage("/casError", CasLoginErrorPage.class);
242: // bookmarkable url for viewing items
243: mount(new IndexedParamUrlCodingStrategy("/item",
244: ItemViewPage.class));
245: }
246:
247: @Override
248: public Session newSession(Request request, Response response) {
249: return new JtracSession(JtracApplication.this , request);
250: }
251:
252: public Class getHomePage() {
253: return DashboardPage.class;
254: }
255:
256: public User authenticate(String loginName, String password) {
257: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
258: loginName, password);
259: AuthenticationManager am = (AuthenticationManager) applicationContext
260: .getBean("authenticationManager");
261: try {
262: Authentication authentication = am.authenticate(token);
263: return (User) authentication.getPrincipal();
264: } catch (AuthenticationException ae) {
265: logger.debug("acegi authentication failed: " + ae);
266: return null;
267: }
268: }
269:
270: private boolean attemptRememberMeAutoLogin() {
271: logger.debug("checking cookies for remember-me auto login");
272: Cookie[] cookies = ((WebRequest) RequestCycle.get()
273: .getRequest()).getCookies();
274: if (cookies == null) {
275: logger.debug("no cookies found");
276: return false;
277: }
278: for (Cookie c : cookies) {
279: if (logger.isDebugEnabled()) {
280: logger.debug("examining cookie: "
281: + WebUtils.getDebugStringForCookie(c));
282: }
283: if (!c.getName().equals("jtrac")) {
284: continue;
285: }
286: String value = c.getValue();
287: logger.debug("found jtrac cookie: " + value);
288: if (value == null) {
289: continue;
290: }
291: int index = value.indexOf(':');
292: if (index == -1) {
293: continue;
294: }
295: String loginName = value.substring(0, index);
296: String encodedPassword = value.substring(index + 1);
297: logger.debug("valid cookie, attempting authentication");
298: User user = (User) getJtrac().loadUserByUsername(loginName);
299: if (encodedPassword.equals(user.getPassword())) {
300: ((JtracSession) Session.get()).setUser(user);
301: logger.debug("remember me login success");
302: return true;
303: }
304: }
305: // no valid cookies were found
306: return false;
307: }
308:
309: }
|