001/*
002 *  Copyright 2016 Anyware Services
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 */
016package org.ametys.plugins.core.impl.authentication;
017
018import java.util.Map;
019
020import org.apache.avalon.framework.context.Context;
021import org.apache.avalon.framework.context.ContextException;
022import org.apache.avalon.framework.context.Contextualizable;
023import org.apache.cocoon.components.ContextHelper;
024import org.apache.cocoon.environment.ObjectModelHelper;
025import org.apache.cocoon.environment.Redirector;
026import org.apache.commons.lang3.StringUtils;
027
028import org.ametys.core.authentication.AbstractCredentialProvider;
029import org.ametys.core.authentication.NonBlockingCredentialProvider;
030import org.ametys.core.user.UserIdentity;
031
032/**
033 * This manager gets the credentials given by a J2EE filter authentication.<br>
034 * The filter must set the 'remote user' header into the request.<br>
035 * <br>
036 * This manager can not get the password of the connected user: the user is 
037 * already authentified. This manager should not be associated with an
038 * <code>AuthenticableBaseUser</code>
039 */
040public class RemoteUserCredentialProvider extends AbstractCredentialProvider implements NonBlockingCredentialProvider, Contextualizable
041{
042    /** Name of the parameter holding the authentication realm */
043    private static final String __PARAM_REALM = "runtime.authentication.remote.realm";
044    
045    /** Name of the parameter holding the header name */
046    private static final String __PARAM_HEADER_NAME = "runtime.authentication.remote.header.name";
047
048    /** The realm */
049    protected String _realm;
050
051    /** The header name */
052    protected String _headerName;
053    
054    private Context _context;
055    
056    @Override
057    public void contextualize(Context context) throws ContextException
058    {
059        _context = context;
060    }
061    
062    @Override
063    public void init(String id, String cpModelId, Map<String, Object> paramValues, String label) throws Exception
064    {
065        super.init(id, cpModelId, paramValues, label);
066        _realm = (String) paramValues.get(__PARAM_REALM);
067        _headerName = (String) paramValues.get(__PARAM_HEADER_NAME);
068    }
069
070    @Override
071    public boolean nonBlockingIsStillConnected(UserIdentity userIdentity, Redirector redirector) throws Exception
072    {
073        // this manager is always valid
074        return true;
075    }
076
077    @Override
078    public boolean nonBlockingGrantAnonymousRequest()
079    {
080        // this implementation does not have any particular request
081        // to take into account
082        return false;
083    }
084
085    @Override
086    public UserIdentity nonBlockingGetUserIdentity(Redirector redirector) throws Exception
087    {
088        Map objectModel = ContextHelper.getObjectModel(_context);
089        String remoteLogin = ObjectModelHelper.getRequest(objectModel).getHeader(_headerName);
090        if (remoteLogin == null)
091        {
092            getLogger().error("Remote User is null! Missing filter?");
093            return null;
094        }
095
096        if (StringUtils.isNotBlank(_realm))
097        {
098            int begin = remoteLogin.indexOf("\\");
099            if (begin <= 0)
100            {
101                /* Domain authentication but non compliant */
102                getLogger().error("Remote User '{}' does not match realm\\login", remoteLogin);
103                return null;
104            }
105    
106            String userLogin = remoteLogin.substring(begin + 1);
107            String userRealm = remoteLogin.substring(0, begin);
108            
109            if (!_realm.equals(userRealm))
110            {
111                getLogger().error("Remote user realm '{}' does not match application realm '{}'", userRealm, _realm);
112                return null;
113            }
114            
115            return new UserIdentity(userLogin, null);
116        }
117        else
118        {
119            return new UserIdentity(remoteLogin, null);
120        }
121
122
123    }
124
125    @Override
126    public void nonBlockingUserNotAllowed(Redirector redirector)
127    {
128        // nothing to do
129    }
130
131    @Override
132    public void nonBlockingUserAllowed(UserIdentity userIdentity, Redirector redirector)
133    {
134        // nothing to do
135    }
136}