de.thorstenberger.examServer.ws.remoteusermanager.ShibbolethAuthenticationProvider.java Source code

Java tutorial

Introduction

Here is the source code for de.thorstenberger.examServer.ws.remoteusermanager.ShibbolethAuthenticationProvider.java

Source

/*
    
Copyright (C) 2009 Steffen Dienst
    
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
This program 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 General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package de.thorstenberger.examServer.ws.remoteusermanager;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.AuthenticationServiceException;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.providers.AuthenticationProvider;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;

import de.thorstenberger.examServer.model.User;
import de.thorstenberger.examServer.service.ConfigManager;
import de.thorstenberger.examServer.service.RoleManager;
import de.thorstenberger.examServer.service.UserExistsException;
import de.thorstenberger.examServer.service.UserManager;
import de.thorstenberger.examServer.util.StringUtil;
import de.thorstenberger.examServer.ws.remoteusermanager.client.UserBean;

/**
 * Rudimentary start of a shibboleth client implementation. Misses verification of the shibboleth response atm. NOT
 * WORKING YET!
 *
 * @author Steffen Dienst
 *
 */
public class ShibbolethAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider
        implements AuthenticationProvider, MessageSourceAware {
    private final Log log = LogFactory.getLog(ShibbolethAuthenticationProvider.class);

    private MessageSourceAccessor messageSourceAccessor;
    private final ConfigManager configManager;
    private final UserManager userManager;
    private final RoleManager roleManager;

    private boolean forcePrincipalAsString = false;

    /**
     *
     */
    public ShibbolethAuthenticationProvider(final ConfigManager configManager, final UserManager userManager,
            final RoleManager roleManager) {
        this.configManager = configManager;
        this.userManager = userManager;
        this.roleManager = roleManager;
    }

    /* (non-Javadoc)
     * @see org.acegisecurity.providers.AuthenticationProvider#authenticate(org.acegisecurity.Authentication)
     */
    @Override
    public Authentication authenticate(final Authentication authentication) throws AuthenticationException {

        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
                messageSourceAccessor.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
                        "Only UsernamePasswordAuthenticationToken is supported"));

        if (!configManager.isStudentsLoginEnabled()) {
            throw new AuthenticationServiceException("Login disabled for student role.");
        }

        if (configManager.getHTTPAuthURL() != null && configManager.getHTTPAuthURL().length() > 0) {

            // Determine username
            final String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
                    : authentication.getName();
            final String password = (authentication.getCredentials() == null) ? "NONE_PROVIDED"
                    : (String) authentication.getCredentials();

            UserBean userBean;
            try {

                // verify url
                URL url = null;
                try {
                    url = new URL(configManager.getHTTPAuthURL());
                } catch (final MalformedURLException e1) {
                    throw new AuthenticationServiceException("URL-Error for HTTP-Auth.");
                }

                userBean = getRemoteUserInfos(url, username, password);

                if (!userBean.getRole().equals("student")) {
                    throw new AuthenticationServiceException("Only student role allowed.");
                }

                try {

                    userManager.getUserByUsername(userBean.getLogin());

                } catch (final UsernameNotFoundException e) {
                    final User user = new User();
                    user.setEnabled(true);
                    user.setUsername(userBean.getLogin());
                    user.setFirstName(userBean.getFirstName() == null ? "" : userBean.getFirstName());
                    user.setLastName(userBean.getName() == null ? "" : userBean.getName());
                    user.setEmail(userBean.getEmail() == null ? "" : userBean.getEmail());
                    user.setPassword(StringUtil.encodePassword(userBean.getPassword(), "SHA"));
                    user.addRole(roleManager.getRole("student"));
                    try {
                        userManager.saveUser(user);
                    } catch (final UserExistsException e2) {
                        // should not happen
                        throw new RuntimeException(e2);
                    }
                }
            } catch (final AuthenticationServiceException e) {
                throw new BadCredentialsException(
                        messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials",
                                "Invalid username or password."));
            }

            final UserDetails userDetails = userManager.getUserByUsername(username);

            Object principalToReturn = userDetails;

            if (forcePrincipalAsString) {
                principalToReturn = userDetails.getUsername();
            }

            return createSuccessAuthentication(principalToReturn, authentication, userDetails);

        }

        return null;

    }

    private UserBean getRemoteUserInfos(final URL url, final String login, final String pwd)
            throws AuthenticationServiceException {
        try {
            final boolean auth = authenticateUser(url, login, pwd);
            if (auth) {
                String email = null;
                if (configManager.getHTTPAuthMail() != null && configManager.getHTTPAuthMail().length() > 0) {
                    email = login + '@' + configManager.getHTTPAuthMail();
                }
                // now we know that at least login and password are correct, so we create the user bean
                final UserBean bean = new UserBean(email, login, null, pwd, "student", null);
                return bean;
            }
            throw new AuthenticationServiceException("");
        } catch (final IOException e) {
            throw new AuthenticationServiceException(e.getMessage(), e);
        }
    }

    private boolean authenticateUser(final URL url, final String login, final String pwd) throws IOException {
        final HttpClient client = new HttpClient();
        client.getParams().setCookiePolicy(CookiePolicy.RFC_2109);
        client.getState().setCredentials(new AuthScope(url.getHost(), AuthScope.ANY_PORT, AuthScope.ANY_REALM),
                new UsernamePasswordCredentials(login, pwd));

        final GetMethod authget = new GetMethod(url.toString());

        authget.setDoAuthentication(true);

        String response = null;
        try {
            final int status = client.executeMethod(authget);

            // check if successful
            if (status == 200) {
                response = authget.getResponseBodyAsString();

                return isValidAuthentication(extractSAMLResponse(response));
            }
        } finally {
            // release any connection resources used by the method
            authget.releaseConnection();
        }

        return false;
    }

    /**
     * Verify the signed SAMLResponse xml file using the signature within.
     *
     * @param samlXml
     *            SAMLResponse
     * @return true if this authentication is valid
     */
    private boolean isValidAuthentication(final String samlXml) {
        // TODO Auto-generated method stub
        // log.warn("Invalid IdP authentication: "+samlXml);
        return true;
    }

    /**
     * @param xml
     * @return
     */
    private String extractSAMLResponse(final String html) {
        if (html == null) {
            throw new AuthenticationServiceException("Invalid authentication response from the shibboleth IdP!");
        } else {

            return null;
        }
    }

    /* (non-Javadoc)
     * @see org.acegisecurity.providers.AuthenticationProvider#supports(java.lang.Class)
     */
    @Override
    public boolean supports(final Class authentication) {
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }

    /* (non-Javadoc)
     * @see org.springframework.context.MessageSourceAware#setMessageSource(org.springframework.context.MessageSource)
     */
    @Override
    public void setMessageSource(final MessageSource messageSource) {
        this.messageSourceAccessor = new MessageSourceAccessor(messageSource);
    }

    /* (non-Javadoc)
     * @see org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider#additionalAuthenticationChecks(org.acegisecurity.userdetails.UserDetails, org.acegisecurity.providers.UsernamePasswordAuthenticationToken)
     */
    @Override
    protected void additionalAuthenticationChecks(final UserDetails arg0,
            final UsernamePasswordAuthenticationToken arg1) throws AuthenticationException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider#retrieveUser(java.lang.String, org.acegisecurity.providers.UsernamePasswordAuthenticationToken)
     */
    @Override
    protected UserDetails retrieveUser(final String arg0, final UsernamePasswordAuthenticationToken arg1)
            throws AuthenticationException {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * @return Returns the forcePrincipalAsString.
     */
    @Override
    public boolean isForcePrincipalAsString() {
        return forcePrincipalAsString;
    }

    /**
     * @param forcePrincipalAsString The forcePrincipalAsString to set.
     */
    @Override
    public void setForcePrincipalAsString(final boolean forcePrincipalAsString) {
        this.forcePrincipalAsString = forcePrincipalAsString;
    }

}