de.thm.arsnova.controller.LoginController.java Source code

Java tutorial

Introduction

Here is the source code for de.thm.arsnova.controller.LoginController.java

Source

/*
 * This file is part of ARSnova Backend.
 * Copyright (C) 2012-2015 The ARSnova Team
 *
 * ARSnova Backend 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 3 of the License, or
 * (at your option) any later version.
 *
 * ARSnova Backend 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, see <http://www.gnu.org/licenses/>.
 */
package de.thm.arsnova.controller;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.scribe.up.provider.impl.FacebookProvider;
import org.scribe.up.provider.impl.Google2Provider;
import org.scribe.up.provider.impl.TwitterProvider;
import org.scribe.up.session.HttpUserSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.cas.authentication.CasAuthenticationToken;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.token.Sha512DigestUtils;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.RedirectView;

import de.thm.arsnova.entities.ServiceDescription;
import de.thm.arsnova.entities.Session;
import de.thm.arsnova.entities.User;
import de.thm.arsnova.exceptions.UnauthorizedException;
import de.thm.arsnova.services.IUserService;
import de.thm.arsnova.services.UserSessionService;

@Controller
public class LoginController extends AbstractController {

    private static final int MAX_USERNAME_LENGTH = 15;
    private static final int MAX_GUESTHASH_LENGTH = 10;

    @Value("${api.path:}")
    private String apiPath;
    @Value("${customization.path}")
    private String customizationPath;

    @Value("${security.guest.enabled}")
    private String guestEnabled;
    @Value("${security.guest.lecturer.enabled}")
    private String guestLecturerEnabled;
    @Value("${security.guest.order}")
    private int guestOrder;

    @Value("${security.custom-login.enabled}")
    private String customLoginEnabled;
    @Value("${security.custom-login.title:University}")
    private String customLoginTitle;
    @Value("${security.custom-login.login-dialog-path}")
    private String customLoginDialog;
    @Value("${security.custom-login.image:}")
    private String customLoginImage;
    @Value("${security.custom-login.order}")
    private int customLoginOrder;

    @Value("${security.user-db.enabled}")
    private String dbAuthEnabled;
    @Value("${security.user-db.title:ARSnova}")
    private String dbAuthTitle;
    @Value("${security.user-db.login-dialog-path}")
    private String dbAuthDialog;
    @Value("${security.user-db.image:}")
    private String dbAuthImage;
    @Value("${security.user-db.order}")
    private int dbAuthOrder;

    @Value("${security.ldap.enabled}")
    private String ldapEnabled;
    @Value("${security.ldap.title:LDAP}")
    private String ldapTitle;
    @Value("${security.ldap.login-dialog-path}")
    private String ldapDialog;
    @Value("${security.ldap.image:}")
    private String ldapImage;
    @Value("${security.ldap.order}")
    private int ldapOrder;

    @Value("${security.cas.enabled}")
    private String casEnabled;
    @Value("${security.cas.title:CAS}")
    private String casTitle;
    @Value("${security.cas.image:}")
    private String casImage;
    @Value("${security.cas.order}")
    private int casOrder;

    @Value("${security.facebook.enabled}")
    private String facebookEnabled;
    @Value("${security.facebook.order}")
    private int facebookOrder;

    @Value("${security.google.enabled}")
    private String googleEnabled;
    @Value("${security.google.order}")
    private int googleOrder;

    @Value("${security.twitter.enabled}")
    private String twitterEnabled;
    @Value("${security.twitter.order}")
    private int twitterOrder;

    @Autowired(required = false)
    private DaoAuthenticationProvider daoProvider;

    @Autowired(required = false)
    private TwitterProvider twitterProvider;

    @Autowired(required = false)
    private Google2Provider googleProvider;

    @Autowired(required = false)
    private FacebookProvider facebookProvider;

    @Autowired(required = false)
    private LdapAuthenticationProvider ldapAuthenticationProvider;

    @Autowired(required = false)
    private CasAuthenticationEntryPoint casEntryPoint;

    @Autowired
    private IUserService userService;

    @Autowired
    private UserSessionService userSessionService;

    public static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);

    @RequestMapping(value = { "/auth/login", "/doLogin" }, method = { RequestMethod.POST, RequestMethod.GET })
    public void doLogin(@RequestParam("type") final String type,
            @RequestParam(value = "user", required = false) String username,
            @RequestParam(required = false) final String password,
            @RequestParam(value = "role", required = false) final UserSessionService.Role role,
            final HttpServletRequest request, final HttpServletResponse response) throws IOException {
        String addr = request.getRemoteAddr();
        if (userService.isBannedFromLogin(addr)) {
            response.sendError(429, "Too Many Requests");

            return;
        }

        userSessionService.setRole(role);

        if ("arsnova".equals(type)) {
            Authentication authRequest = new UsernamePasswordAuthenticationToken(username, password);
            try {
                Authentication auth = daoProvider.authenticate(authRequest);
                if (auth.isAuthenticated()) {
                    SecurityContextHolder.getContext().setAuthentication(auth);
                    request.getSession(true).setAttribute(
                            HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
                            SecurityContextHolder.getContext());

                    return;
                }
            } catch (AuthenticationException e) {
                LOGGER.info("Authentication failed: {}", e.getMessage());
            }

            userService.increaseFailedLoginCount(addr);
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
        } else if ("ldap".equals(type)) {
            if (!"".equals(username) && !"".equals(password)) {
                org.springframework.security.core.userdetails.User user = new org.springframework.security.core.userdetails.User(
                        username, password, true, true, true, true, this.getAuthorities());

                Authentication token = new UsernamePasswordAuthenticationToken(user, password, getAuthorities());
                try {
                    Authentication auth = ldapAuthenticationProvider.authenticate(token);
                    if (auth.isAuthenticated()) {
                        SecurityContextHolder.getContext().setAuthentication(token);
                        request.getSession(true).setAttribute(
                                HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
                                SecurityContextHolder.getContext());

                        return;
                    }
                    LOGGER.info("LDAPLOGIN: {}", auth.isAuthenticated());
                } catch (AuthenticationException e) {
                    LOGGER.info("No LDAP login: {}", e);
                }

                userService.increaseFailedLoginCount(addr);
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
            }
        } else if ("guest".equals(type)) {
            List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
            authorities.add(new SimpleGrantedAuthority("ROLE_GUEST"));
            if (username == null || !username.startsWith("Guest") || username.length() != MAX_USERNAME_LENGTH) {
                username = "Guest"
                        + Sha512DigestUtils.shaHex(request.getSession().getId()).substring(0, MAX_GUESTHASH_LENGTH);
            }
            org.springframework.security.core.userdetails.User user = new org.springframework.security.core.userdetails.User(
                    username, "", true, true, true, true, authorities);
            Authentication token = new UsernamePasswordAuthenticationToken(user, null, authorities);

            SecurityContextHolder.getContext().setAuthentication(token);
            request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
                    SecurityContextHolder.getContext());
        }
    }

    @RequestMapping(value = { "/auth/dialog" }, method = RequestMethod.GET)
    @ResponseBody
    public View dialog(@RequestParam("type") final String type,
            @RequestParam(value = "successurl", defaultValue = "/") String successUrl,
            @RequestParam(value = "failureurl", defaultValue = "/") String failureUrl,
            final HttpServletRequest request, final HttpServletResponse response)
            throws IOException, ServletException {
        View result = null;

        /* Use URLs from a request parameters for redirection as long as the
         * URL is not absolute (to prevent abuse of the redirection). */
        if (UrlUtils.isAbsoluteUrl(successUrl)) {
            successUrl = "/";
        }
        if (UrlUtils.isAbsoluteUrl(failureUrl)) {
            failureUrl = "/";
        }

        String serverUrl = request.getScheme() + "://" + request.getServerName();
        /* Handle proxy
         * TODO: It might be better, to support the proposed standard: http://tools.ietf.org/html/rfc7239 */
        int port = "".equals(request.getHeader("X-Forwarded-Port"))
                ? Integer.valueOf(request.getHeader("X-Forwarded-Port"))
                : request.getServerPort();
        if ("https".equals(request.getScheme())) {
            if (443 != port) {
                serverUrl = serverUrl + ":" + String.valueOf(port);
            }
        } else {
            if (80 != port) {
                serverUrl = serverUrl + ":" + String.valueOf(port);
            }
        }

        request.getSession().setAttribute("ars-login-success-url", serverUrl + successUrl);
        request.getSession().setAttribute("ars-login-failure-url", serverUrl + failureUrl);

        if ("cas".equals(type)) {
            casEntryPoint.commence(request, response, null);
        } else if ("twitter".equals(type)) {
            final String authUrl = twitterProvider.getAuthorizationUrl(new HttpUserSession(request));
            result = new RedirectView(authUrl);
        } else if ("facebook".equals(type)) {
            facebookProvider.setFields("id,link");
            facebookProvider.setScope("");
            final String authUrl = facebookProvider.getAuthorizationUrl(new HttpUserSession(request));
            result = new RedirectView(authUrl);
        } else if ("google".equals(type)) {
            final String authUrl = googleProvider.getAuthorizationUrl(new HttpUserSession(request));
            result = new RedirectView(authUrl);
        }

        return result;
    }

    @RequestMapping(value = { "/auth/", "/whoami" }, method = RequestMethod.GET)
    @ResponseBody
    public User whoami() {
        userSessionService.setUser(userService.getCurrentUser());
        return userService.getCurrentUser();
    }

    @RequestMapping(value = { "/auth/logout", "/logout" }, method = { RequestMethod.POST, RequestMethod.GET })
    public View doLogout(final HttpServletRequest request) {
        final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        userService.removeUserFromMaps(userService.getCurrentUser());
        request.getSession().invalidate();
        SecurityContextHolder.clearContext();
        if (auth instanceof CasAuthenticationToken) {
            if ("".equals(apiPath)) {
                apiPath = request.getContextPath();
            }
            return new RedirectView(apiPath + "/j_spring_cas_security_logout");
        }
        return new RedirectView(request.getHeader("referer") != null ? request.getHeader("referer") : "/");
    }

    @RequestMapping(value = { "/auth/services" }, method = RequestMethod.GET)
    @ResponseBody
    public List<ServiceDescription> getServices(final HttpServletRequest request) {
        List<ServiceDescription> services = new ArrayList<ServiceDescription>();

        if ("".equals(apiPath)) {
            apiPath = request.getContextPath();
        }
        /* The first parameter is replaced by the backend, the second one by the frondend */
        String dialogUrl = apiPath + "/auth/dialog?type={0}&successurl='{0}'";

        if ("true".equals(guestEnabled)) {
            ServiceDescription sdesc = new ServiceDescription("guest", "Guest", null);
            sdesc.setOrder(guestOrder);
            if (!"true".equals(guestLecturerEnabled)) {
                sdesc.setAllowLecturer(false);
            }
            services.add(sdesc);
        }

        if ("true".equals(customLoginEnabled) && !"".equals(customLoginDialog)) {
            ServiceDescription sdesc = new ServiceDescription("custom", customLoginTitle,
                    customizationPath + "/" + customLoginDialog + "?redirect={0}", customLoginImage);
            sdesc.setOrder(customLoginOrder);
            services.add(sdesc);
        }

        if ("true".equals(dbAuthEnabled) && !"".equals(dbAuthDialog)) {
            ServiceDescription sdesc = new ServiceDescription("arsnova", dbAuthTitle,
                    customizationPath + "/" + dbAuthDialog + "?redirect={0}", dbAuthImage);
            sdesc.setOrder(dbAuthOrder);
            services.add(sdesc);
        }

        if ("true".equals(ldapEnabled) && !"".equals(ldapDialog)) {
            ServiceDescription sdesc = new ServiceDescription("ldap", ldapTitle,
                    customizationPath + "/" + ldapDialog + "?redirect={0}", ldapImage);
            sdesc.setOrder(ldapOrder);
            services.add(sdesc);
        }

        if ("true".equals(casEnabled)) {
            ServiceDescription sdesc = new ServiceDescription("cas", casTitle,
                    MessageFormat.format(dialogUrl, "cas"));
            sdesc.setOrder(casOrder);
            services.add(sdesc);
        }

        if ("true".equals(facebookEnabled)) {
            ServiceDescription sdesc = new ServiceDescription("facebook", "Facebook",
                    MessageFormat.format(dialogUrl, "facebook"));
            sdesc.setOrder(facebookOrder);
            services.add(sdesc);
        }

        if ("true".equals(googleEnabled)) {
            ServiceDescription sdesc = new ServiceDescription("google", "Google",
                    MessageFormat.format(dialogUrl, "google"));
            sdesc.setOrder(googleOrder);
            services.add(sdesc);
        }

        if ("true".equals(twitterEnabled)) {
            ServiceDescription sdesc = new ServiceDescription("twitter", "Twitter",
                    MessageFormat.format(dialogUrl, "twitter"));
            sdesc.setOrder(twitterOrder);
            services.add(sdesc);
        }

        return services;
    }

    private Collection<GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
        authList.add(new SimpleGrantedAuthority("ROLE_USER"));
        return authList;
    }

    @RequestMapping(value = { "/test/me" }, method = RequestMethod.GET)
    @ResponseBody
    public User me() {
        final User me = userSessionService.getUser();
        if (me == null) {
            throw new UnauthorizedException();
        }
        return me;
    }

    @RequestMapping(value = { "/test/mysession" }, method = RequestMethod.GET)
    @ResponseBody
    public Session mysession() {
        final Session mysession = userSessionService.getSession();
        if (mysession == null) {
            throw new UnauthorizedException();
        }
        return mysession;
    }

    @RequestMapping(value = { "/test/myrole" }, method = RequestMethod.GET)
    @ResponseBody
    public UserSessionService.Role myrole() {
        final UserSessionService.Role myrole = userSessionService.getRole();
        if (myrole == null) {
            throw new UnauthorizedException();
        }
        return myrole;
    }
}