001/*******************************************************************************
002 * Copyright 2015 The MITRE Corporation
003 *   and the MIT Kerberos and Internet Trust Consortium
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *   http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *******************************************************************************/
017/**
018 * 
019 */
020package org.mitre.openid.connect.web;
021
022import java.lang.reflect.Type;
023
024import javax.servlet.http.HttpServletRequest;
025import javax.servlet.http.HttpServletResponse;
026
027import org.mitre.openid.connect.model.OIDCAuthenticationToken;
028import org.mitre.openid.connect.model.UserInfo;
029import org.mitre.openid.connect.service.UserInfoService;
030import org.springframework.beans.factory.annotation.Autowired;
031import org.springframework.security.core.Authentication;
032import org.springframework.security.core.GrantedAuthority;
033import org.springframework.security.core.context.SecurityContextHolder;
034import org.springframework.web.servlet.ModelAndView;
035import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
036import org.springframework.web.servlet.view.RedirectView;
037import org.springframework.web.servlet.view.UrlBasedViewResolver;
038
039import com.google.common.base.Strings;
040import com.google.gson.Gson;
041import com.google.gson.GsonBuilder;
042import com.google.gson.JsonElement;
043import com.google.gson.JsonPrimitive;
044import com.google.gson.JsonSerializationContext;
045import com.google.gson.JsonSerializer;
046
047/**
048 * Injects the UserInfo object for the current user into the current model's context, if both exist. Allows JSPs and the like to call "userInfo.name" and other fields.
049 * 
050 * @author jricher
051 *
052 */
053public class UserInfoInterceptor extends HandlerInterceptorAdapter {
054
055        private Gson gson = new GsonBuilder()
056        .registerTypeHierarchyAdapter(GrantedAuthority.class, new JsonSerializer<GrantedAuthority>() {
057                @Override
058                public JsonElement serialize(GrantedAuthority src, Type typeOfSrc, JsonSerializationContext context) {
059                        return new JsonPrimitive(src.getAuthority());
060                }
061        })
062        .create();
063
064        @Autowired (required = false)
065        private UserInfoService userInfoService;
066
067        @Override
068        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
069
070                if (modelAndView != null && !modelAndView.getModel().containsKey("userInfo")) { // skip checking at all if we have no model and view to hand the user to
071                        // or if there's already a userInfo object in there
072
073                        // TODO: this is a patch to get around a potential information leak from #492
074
075                        if (modelAndView.getView() instanceof RedirectView) {
076                                // don't add them
077                        } else {
078                                if (Strings.isNullOrEmpty(modelAndView.getViewName())) {
079                                        // add them
080                                        injectUserInfo(modelAndView);
081                                } else {
082                                        if (modelAndView.getViewName().startsWith(UrlBasedViewResolver.FORWARD_URL_PREFIX) || 
083                                                        modelAndView.getViewName().startsWith(UrlBasedViewResolver.REDIRECT_URL_PREFIX)) {
084                                                // don't add them
085                                        } else {
086                                                // add them
087                                                injectUserInfo(modelAndView);
088                                        }
089                                }
090                        }
091                }
092        }
093
094        private void injectUserInfo(ModelAndView modelAndView) {
095                Authentication auth = SecurityContextHolder.getContext().getAuthentication();
096
097                if (auth instanceof Authentication && !modelAndView.getModel().containsKey("userAuthorities")){
098                        modelAndView.addObject("userAuthorities", gson.toJson(auth.getAuthorities()));
099                }
100
101                if (auth instanceof OIDCAuthenticationToken) {
102                        // if they're logging into this server from a remote OIDC server, pass through their user info
103                        OIDCAuthenticationToken oidc = (OIDCAuthenticationToken) auth;
104                        if (oidc.getUserInfo() != null) {
105                                modelAndView.addObject("userInfo", oidc.getUserInfo());
106                                modelAndView.addObject("userInfoJson", oidc.getUserInfo().toJson());
107                        } else {
108                                modelAndView.addObject("userInfo", null);
109                                modelAndView.addObject("userInfoJson", "null");
110                        }
111                } else {
112                        // don't bother checking if we don't have a principal or a userInfoService to work with
113                        if (auth != null && auth.getName() != null && userInfoService != null) {
114
115                                // try to look up a user based on the principal's name
116                                UserInfo user = userInfoService.getByUsername(auth.getName());
117
118                                // if we have one, inject it so views can use it
119                                if (user != null) {
120                                        modelAndView.addObject("userInfo", user);
121                                        modelAndView.addObject("userInfoJson", user.toJson());
122                                }
123                        }
124                }
125        }
126
127}