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 java.io.IOException;
022import java.security.Principal;
023
024import javax.security.auth.callback.Callback;
025import javax.security.auth.callback.UnsupportedCallbackException;
026import javax.security.auth.login.FailedLoginException;
027import javax.security.auth.login.LoginException;
028import javax.servlet.http.HttpServletRequest;
029import javax.servlet.http.HttpSession;
030
031import org.apache.log4j.Logger;
032
033import org.apache.wiki.auth.WikiPrincipal;
034
035/**
036 * <p>
037 * Logs in a user by extracting authentication data from an Http servlet
038 * session. First, the module tries to extract a Principal object out of the
039 * request directly using the servlet requests's <code>getUserPrincipal()</code>
040 * method. If one is found, authentication succeeds. If there is no
041 * Principal in the request, try calling <code>getRemoteUser()</code>. If
042 * the <code>remoteUser</code> exists but the UserDatabase can't find a matching
043 * profile, a generic WikiPrincipal is created with this value. If neither
044 * <code>userPrincipal</code> nor <code>remoteUser</code> exist in the request, the login fails.
045 * </p>
046 * <p>
047 * This module must be used with a CallbackHandler that supports the following
048 * Callback types:
049 * </p>
050 * <ol>
051 * <li>{@link HttpRequestCallback} - supplies the Http request object, from
052 * which the getRemoteUser and getUserPrincipal are extracted</li>
053 * <li>{@link UserDatabaseCallback} - supplies the user database for looking up
054 * the value of getRemoteUser</li>
055 * </ol>
056 * <p>
057 * After authentication, the Subject will contain the Principal that
058 * represents the logged-in user.</p>
059 *
060 * @since 2.3
061 */
062public class WebContainerLoginModule extends AbstractLoginModule
063{
064
065    protected static final Logger log      = Logger.getLogger( WebContainerLoginModule.class );
066
067    /**
068     * Logs in the user.
069     * @see javax.security.auth.spi.LoginModule#login()
070     * 
071     * @return {@inheritDoc}
072     * @throws {@inheritDoc}
073     */
074    public boolean login() throws LoginException
075    {
076        HttpRequestCallback rcb = new HttpRequestCallback();
077        Callback[] callbacks = new Callback[] { rcb };
078        String userId = null;
079
080        try
081        {
082            // First, try to extract a Principal object out of the request
083            // directly. If we find one, we're done.
084            m_handler.handle( callbacks );
085            HttpServletRequest request = rcb.getRequest();
086            if ( request == null )
087            {
088                throw new LoginException( "No Http request supplied." );
089            }
090            HttpSession session = request.getSession(false);
091            String sid = (session == null) ? NULL : session.getId();
092            Principal principal = request.getUserPrincipal();
093            if ( principal == null )
094            {
095                // If no Principal in request, try the remoteUser
096                if ( log.isDebugEnabled() )
097                {
098                    log.debug( "No userPrincipal found for session ID=" + sid);
099                }
100                userId = request.getRemoteUser();
101                if ( userId == null )
102                {
103                    if ( log.isDebugEnabled() )
104                    {
105                        log.debug( "No remoteUser found for session ID=" + sid);
106                    }
107                    throw new FailedLoginException( "No remote user found" );
108                }
109                principal = new WikiPrincipal( userId, WikiPrincipal.LOGIN_NAME );
110            }
111            if ( log.isDebugEnabled() )
112            {
113                log.debug("Logged in container principal " + principal.getName() + "." );
114            }
115            m_principals.add( principal );
116
117            return true;
118        }
119        catch( IOException e )
120        {
121            log.error( "IOException: " + e.getMessage() );
122            return false;
123        }
124        catch( UnsupportedCallbackException e )
125        {
126            log.error( "UnsupportedCallbackException: " + e.getMessage() );
127            return false;
128        }
129    }
130}