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}