edu.uchicago.duo.web.DuoDeviceMgmtController.java Source code

Java tutorial

Introduction

Here is the source code for edu.uchicago.duo.web.DuoDeviceMgmtController.java

Source

/**
 * Copyright 2014 University of Chicago
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 * Author: Daniel Yu <danielyu@uchicago.edu>
 */
package edu.uchicago.duo.web;

import edu.uchicago.duo.domain.DuoPersonObj;
import edu.uchicago.duo.service.DuoObjInterface;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.sql.Date;
import java.text.SimpleDateFormat;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.SmartValidator;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
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.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
@RequestMapping("/secure/devicemgmt")
@SessionAttributes("DuoPerson")
public class DuoDeviceMgmtController {

    protected final Log logger = LogFactory.getLog(getClass());
    //
    @Autowired
    private DuoObjInterface duoUsrService;
    //
    @Autowired
    private DuoObjInterface duoPhoneService;
    //
    @Autowired
    private DuoObjInterface duoTabletService;
    //
    @Autowired
    private DuoObjInterface duoTokenService;
    //
    @Autowired
    SmartValidator validator;

    /**
     * **********************************************************
     *
     * Private Methods Below
     *
     ***********************************************************
     */
    private String getIPForLog(HttpServletRequest request) {
        String sourceIPAddr = request.getRemoteAddr();
        if (sourceIPAddr == null || sourceIPAddr.startsWith("127.")) {
            sourceIPAddr = request.getHeader("x-forwarded-for");
        }

        sourceIPAddr = "[" + sourceIPAddr + "]";

        return sourceIPAddr;
    }

    /**
     * **********************************************************
     *
     * Spring Controller Methods Below
     *
     ***********************************************************
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
    }

    @RequestMapping(method = RequestMethod.GET)
    public String initForm(HttpServletRequest request, Principal principal, ModelMap model,
            @ModelAttribute DuoPersonObj duoperson, HttpSession session, SessionStatus status) {

        //Below getting SSO Attributes for Shibboleth Support(UChicago)
        //      duoperson.setUsername(principal.getName());
        //      duoperson.setFullName(request.getHeader("givenName")+ " " + request.getHeader("sn"));
        //      duoperson.setEmail(request.getHeader("mail"));
        //      duoperson.setChicagoID(request.getHeader("chicagoID"));

        //Below setting Static Attributes for Local Testing
        duoperson.setUsername("DuoTestUser");
        duoperson.setFullName("DUO Testuser");
        duoperson.setEmail("testuser@duotest.com");
        logger.info("2FA Info - " + getIPForLog(request) + " - " + "Username:" + duoperson.getUsername() + "|SID:"
                + request.getSession().getId());

        String userId = null;

        //Check whether User is already registered in the DUO DB, if yes, record that ID, if not, forward them to first timer portal page.
        if (session.getAttribute("duoUserId") == null) {
            userId = duoUsrService.getObjByParam(duoperson.getUsername(), null, "userId");
            if (userId == null) {
                logger.info("2FA Info - " + getIPForLog(request) + " - " + duoperson.getUsername()
                        + " landed on DeviceMgmt Page without registering with DUO!?");
                status.setComplete();
                return "redirect:/secure";
            }
            //Session Attribute "Duo User ID" - ADDED
            session.setAttribute("duoUserId", userId);
            logger.debug("2FA Debug - " + "Username:" + duoperson.getUsername() + "|DuoUserID:" + userId
                    + "retrieved via DUO API Query");
        } else {
            userId = session.getAttribute("duoUserId").toString();
            logger.debug("2FA Debug - " + "Username:" + duoperson.getUsername() + "|DuoUserID:" + userId
                    + "retrieved via Session Variable");
        }

        //Set Duo UserId
        duoperson.setUser_id(userId);

        //Retrieve all Devices that the user have 
        duoperson.setPhones(duoPhoneService.getAllPhones(userId));
        duoperson.setTablets(duoTabletService.getAllTablets(userId));
        duoperson.setTokens(duoTokenService.getAllTokens(userId));

        //Set DISPLAY model attributes, display related table only if device > 0
        if (duoperson.getPhones().size() > 0) {
            model.addAttribute("displayPhones", true);
            logger.debug("2FA Debug - " + getIPForLog(request) + " - " + duoperson.getUsername() + " has "
                    + duoperson.getPhones().size() + " phones");
        }

        if (duoperson.getTablets().size() > 0) {
            model.addAttribute("displayTablets", true);
            logger.debug("2FA Debug - " + getIPForLog(request) + " - " + duoperson.getUsername() + " has "
                    + duoperson.getPhones().size() + " tablets");
        }

        if (duoperson.getTokens().size() > 0) {
            model.addAttribute("displayTokens", true);
            logger.debug("2FA Debug - " + getIPForLog(request) + " - " + duoperson.getUsername() + " has "
                    + duoperson.getPhones().size() + " tokens");
        }

        //Sync Up All the attributes and push them back into Session Model Attribute
        model.addAttribute("DuoPerson", duoperson);

        //Route DUO Registered User who do not have any devices back to First timer Portal
        if (duoperson.getPhones().isEmpty() && duoperson.getTablets().isEmpty()
                && duoperson.getTokens().isEmpty()) {
            //return form view
            logger.debug("2FA Debug - " + getIPForLog(request) + " - " + duoperson.getUsername()
                    + " has NO registered devices in DUO");

            return "redirect:/secure";
        }

        //return form view
        return "DuoDeviceMgmt";
    }

    @RequestMapping(method = RequestMethod.POST, params = "removedevice")
    public String removeDevice(@ModelAttribute("DuoPerson") DuoPersonObj duoperson, HttpServletRequest request,
            BindingResult result, HttpSession session, SessionStatus status, ModelMap model)
            throws UnsupportedEncodingException, Exception {

        switch (duoperson.getChoosenDevice()) {
        case "Mobile":
        case "Landline":
            logger.info("2FA Info - " + getIPForLog(request) + " - " + duoperson.getUsername()
                    + " is DELETING mobile/landline: " + duoperson.getPhonenumber());
            duoPhoneService.deleteObj(duoperson.getPhone_id(), null);
            break;
        case "Tablet":
            logger.info("2FA Info - " + getIPForLog(request) + " - " + duoperson.getUsername()
                    + " is DELETING tablet: " + duoperson.getTabletName());
            duoTabletService.deleteObj(duoperson.getPhone_id(), null);
            break;
        case "Token":
            logger.info("2FA Info - " + getIPForLog(request) + " - " + duoperson.getUsername()
                    + " is DELETING token: " + duoperson.getTokenSerial());
            duoTokenService.deleteObj(duoperson.getTokenId(), duoperson.getUser_id());
            break;
        }

        //return form view
        return "redirect:/secure/devicemgmt";
    }

    @RequestMapping(method = RequestMethod.POST, params = "sendsmscode")
    public String sendSMSCode(@ModelAttribute("DuoPerson") DuoPersonObj duoperson, BindingResult result,
            HttpSession session, SessionStatus status, ModelMap model, final RedirectAttributes redirectAttributes,
            HttpServletRequest request) throws UnsupportedEncodingException, Exception {

        logger.info("2FA Info - " + getIPForLog(request) + " - " + duoperson.getUsername()
                + " is SMSing passcode to " + duoperson.getPhonenumber());
        duoPhoneService.objActionById(duoperson.getPhone_id(), "passcodeSMS");

        redirectAttributes.addFlashAttribute("smsPhoneNumber", duoperson.getPhonenumber());
        redirectAttributes.addFlashAttribute("smsSent", true);

        //return form view
        return "redirect:/secure/devicemgmt";
    }

    /*
     * No Real Need to do any Sanity Check for what type of OS it is for Re-activation since the DuoDeviceMgmt.jsp will only display activation option for
     * Mobile and Tablet ONLY.
     * Although the Button options via DuoDeviceMgmt are "ACTIVATE" and "REACTIVATE", the code is just treating them the same, which is
     * 
     * 1) Capture the necessary Data and store it in DuoPerson Object: Phone OS, Phone Type, Phone Number 
     * 
     * 2) Delete the existing Phone using the Duo Phone ID 
     */
    @RequestMapping(method = RequestMethod.POST, params = "deviceactivation")
    public String deviceActivation(@ModelAttribute("DuoPerson") DuoPersonObj duoperson, BindingResult result,
            HttpSession session, SessionStatus status, ModelMap model, final RedirectAttributes redirectAttributes,
            HttpServletRequest request) throws UnsupportedEncodingException, Exception {

        String temp = null;

        temp = duoperson.getChoosenDevice();
        duoperson.setChoosenDevice(temp.toLowerCase()); //DUO value is title cased, where our code is not, so just lower all cases

        temp = duoperson.getDeviceOS();
        duoperson.setDeviceOS(temp.toLowerCase()); //DUO value is title cased, where our code is not, so just lower all cases

        logger.info("2FA Info - " + getIPForLog(request) + " - " + "REACTIVATION Process-Delete First:"
                + duoperson.getUsername() + " is DELETING mobile/landline: " + duoperson.getPhonenumber());
        duoPhoneService.deleteObj(duoperson.getPhone_id(), null);
        duoperson.setPhone_id(null);
        logger.info("2FA Info - " + getIPForLog(request) + " - " + "REACTIVATION Process-Device Data:"
                + duoperson.getChoosenDevice() + '|' + duoperson.getPhonenumber() + '|' + duoperson.getDeviceOS()
                + " PID:" + duoperson.getPhone_id() + '|' + duoperson.getTabletName());

        redirectAttributes.addFlashAttribute("devReActivate", true);

        return "redirect:/secure/enrollment/deviceReactivation";

    }

    @RequestMapping(method = RequestMethod.POST, params = "resynctoken")
    public String resyncToken(@RequestParam("resyncAction") final String resyncAction,
            @ModelAttribute("DuoPerson") DuoPersonObj duoperson, BindingResult result, HttpSession session,
            SessionStatus status, ModelMap model, HttpServletRequest request,
            final RedirectAttributes redirectAttributes) throws UnsupportedEncodingException, Exception {

        switch (resyncAction) {
        case "input":
            return "DuoResyncToken";
        case "resync":
            logger.debug("2FA Debug - " + getIPForLog(request) + " - " + duoperson.getUsername() + "|Token: "
                    + duoperson.getTokenSerial() + " |Resync(Code1):" + duoperson.getTokenSyncCode1());
            logger.debug("2FA Debug - " + getIPForLog(request) + " - " + duoperson.getUsername() + "|Token: "
                    + duoperson.getTokenSerial() + " |Resync(Code2):" + duoperson.getTokenSyncCode2());
            logger.debug("2FA Debug - " + getIPForLog(request) + " - " + duoperson.getUsername() + "|Token: "
                    + duoperson.getTokenSerial() + " |Resync(Code3):" + duoperson.getTokenSyncCode3());

            /*
             * Calling validator manually, if using the @validated automatically, see below example:
             * @Validated({DuoPersonObj.TokenResyncValidation.class})
             */
            validator.validate(duoperson, result, DuoPersonObj.TokenResyncValidation.class);

            if (result.hasErrors()) {
                return "DuoResyncToken";
            }
            break;
        }
        duoTokenService.resyncObj(duoperson.getTokenId(), duoperson.getTokenSyncCode1(),
                duoperson.getTokenSyncCode2(), duoperson.getTokenSyncCode3());
        logger.info("2FA Info - " + getIPForLog(request) + " - " + duoperson.getUsername() + "|Token: "
                + duoperson.getTokenSerial() + " RESYNC SUCCESS");
        redirectAttributes.addFlashAttribute("resyncTokenId", duoperson.getTokenId());
        redirectAttributes.addFlashAttribute("resyncTokenSN", duoperson.getTokenSerial());
        redirectAttributes.addFlashAttribute("resyncsuccess", true);

        //return form view
        return "redirect:/secure/devicemgmt";
    }

    @RequestMapping(method = RequestMethod.POST, params = "cancel")
    public String cancel(ModelMap model, @ModelAttribute("DuoPerson") DuoPersonObj duoperson, HttpSession session,
            SessionStatus status) {

        status.setComplete();
        return "redirect:/secure/devicemgmt";
    }

    //May not need it anylonger
    @RequestMapping(method = RequestMethod.POST, params = "home")
    public String goHome(ModelMap model, @ModelAttribute("DuoPerson") DuoPersonObj duoperson, HttpSession session,
            SessionStatus status) {

        status.setComplete();
        return "redirect:/secure";
    }
}