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 */ 019package org.apache.wiki.auth.login; 020 021import org.apache.logging.log4j.LogManager; 022import org.apache.logging.log4j.Logger; 023import org.apache.wiki.auth.WikiPrincipal; 024import org.apache.wiki.util.HttpUtil; 025import org.apache.wiki.util.TextUtil; 026 027import javax.security.auth.callback.Callback; 028import javax.security.auth.callback.UnsupportedCallbackException; 029import javax.security.auth.login.FailedLoginException; 030import javax.security.auth.login.LoginException; 031import jakarta.servlet.http.Cookie; 032import jakarta.servlet.http.HttpServletRequest; 033import jakarta.servlet.http.HttpServletResponse; 034import jakarta.servlet.http.HttpSession; 035import jakarta.servlet.jsp.PageContext; 036import java.io.IOException; 037import org.apache.wiki.WikiEngine; 038 039/** 040 * <p> 041 * Logs in a user based on assertion of a name supplied in a cookie. If the 042 * cookie is not found, authentication fails. 043 * </p> 044 * This module must be used with a CallbackHandler (such as 045 * {@link WebContainerCallbackHandler}) that supports the following Callback 046 * types: 047 * </p> 048 * <ol> 049 * <li>{@link HttpRequestCallback}- supplies the cookie, which should contain 050 * a user name.</li> 051 * </ol> 052 * <p> 053 * After authentication, a generic WikiPrincipal based on the username will be 054 * created and associated with the Subject. 055 * </p> 056 * @see javax.security.auth.spi.LoginModule#commit() 057 * @see CookieAuthenticationLoginModule 058 * @since 2.3 059 */ 060public class CookieAssertionLoginModule extends AbstractLoginModule { 061 062 /** The name of the cookie that gets stored to the user browser. */ 063 public static final String PREFS_COOKIE_NAME = "JSPWikiAssertedName"; 064 065 private static final Logger LOG = LogManager.getLogger( CookieAssertionLoginModule.class ); 066 067 /** 068 * {@inheritDoc} 069 * 070 * Logs in the user by calling back to the registered CallbackHandler with 071 * an HttpRequestCallback. The CallbackHandler must supply the current 072 * servlet HTTP request as its response. 073 * @return the result of the login; if a cookie is 074 * found, this method returns <code>true</code>. If not found, this 075 * method throws a <code>FailedLoginException</code>. 076 * @see javax.security.auth.spi.LoginModule#login() 077 */ 078 @Override 079 public boolean login() throws LoginException { 080 // Otherwise, let's go and look for the cookie! 081 final HttpRequestCallback hcb = new HttpRequestCallback(); 082 final Callback[] callbacks = new Callback[] { hcb }; 083 try { 084 m_handler.handle( callbacks ); 085 final HttpServletRequest request = hcb.getRequest(); 086 final HttpSession session = ( request == null ) ? null : request.getSession( false ); 087 final String sid = ( session == null ) ? NULL : session.getId(); 088 final String name = (request != null) ? getUserCookie( request ) : null; 089 if ( name == null ) { 090 LOG.debug( "No cookie {} present in session ID=: {}", PREFS_COOKIE_NAME, sid ); 091 throw new FailedLoginException( "The user cookie was not found." ); 092 } 093 094 LOG.debug( "Logged in session ID={}; asserted={}", sid, name ); 095 // If login succeeds, commit these principals/roles 096 m_principals.add( new WikiPrincipal( name, WikiPrincipal.FULL_NAME ) ); 097 return true; 098 } catch( final IOException e ) { 099 LOG.error( "IOException: " + e.getMessage() ); 100 return false; 101 } catch( final UnsupportedCallbackException e ) { 102 final String message = "Unable to handle callback, disallowing login."; 103 LOG.error( message, e ); 104 throw new LoginException( message ); 105 } 106 } 107 108 /** 109 * Returns the username cookie value. 110 * 111 * @param request The Servlet request, as usual. 112 * @return the username, as retrieved from the cookie 113 */ 114 public static String getUserCookie( final HttpServletRequest request ) { 115 final String cookie = HttpUtil.retrieveCookieValue( request, PREFS_COOKIE_NAME ); 116 final String usernameCookie = TextUtil.urlDecodeUTF8( cookie ); 117 return usernameCookie!= null && usernameCookie.contains( "-->" ) ? 118 usernameCookie.substring( 0, usernameCookie.indexOf( "-->" ) ) : 119 usernameCookie; 120 } 121 122 /** 123 * Sets the username cookie.The cookie value is URLEncoded in UTF-8. 124 * 125 * @param response The Servlet response 126 * @param name The name to write into the cookie. 127 */ 128 @Deprecated 129 public static void setUserCookie( final HttpServletResponse response, String name ) { 130 name = TextUtil.urlEncodeUTF8( name ); 131 final Cookie userId = new Cookie( PREFS_COOKIE_NAME, name ); 132 //FIXME this should be adjustable 133 userId.setMaxAge( 1001 * 24 * 60 * 60 ); // 1001 days is default. 134 response.addCookie( userId ); 135 } 136 /** 137 * Sets the username cookie.The cookie value is URLEncoded in UTF-8. 138 * 139 * @param context 140 * @param response The Servlet response 141 * @param name The name to write into the cookie. 142 */ 143 public static void setUserCookie( final PageContext context, final HttpServletResponse response, String name ) { 144 name = TextUtil.urlEncodeUTF8(name); 145 final Cookie userId = new Cookie(PREFS_COOKIE_NAME, name); 146 //FIXME this should be adjustable 147 userId.setMaxAge(1001 * 24 * 60 * 60); // 1001 days is default. 148 if ("true".equalsIgnoreCase( 149 WikiEngine.getInstance(context.getServletConfig()). 150 getWikiProperties(). 151 getProperty("jspwiki.securecookie", "false"))) { 152 userId.setHttpOnly(true); 153 userId.setSecure(true); 154 } 155 156 response.addCookie(userId); 157 } 158 159 /** 160 * Removes the user cookie from the response. This makes the user appear again as an anonymous coward. 161 * 162 * @param response The servlet response. 163 */ 164 public static void clearUserCookie( final HttpServletResponse response ) { 165 HttpUtil.clearCookie( response, PREFS_COOKIE_NAME ); 166 } 167 168}