/** * Copyright 2014 IHTSDO * 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. */ package org.ihtsdo.otf.refset.security; import java.util.ArrayList; import java.util.Collection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.token.Token; import org.springframework.security.core.token.TokenService; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.databind.JsonNode; /** * */ public class RefsetIdentityService implements UserDetailsService { private static final Logger LOGGER = LoggerFactory.getLogger(RefsetIdentityService.class); private static final String ROLE_USER = "ROLE_USER"; //private static final String ROLE_ADMIN = "ROLE_ADMIN"; private static final String ROLE_ANONYMOUS = "ROLE_ANONYMOUS"; private static final String APP_NAME = "Refset"; private String otfServiceUrl; private RestTemplate rt; TokenService service; /* (non-Javadoc) * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String) */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User u = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (u.getUsername().equals(username)) { return u; } else { String msg = String.format("User with given user name {} does not exist", username); throw new UsernameNotFoundException(msg); } } protected UserDetails authenticate(String userName, String token) { LOGGER.debug("Authenticating user {} ", userName); User user = getGuestUser(); MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>(); params.add("username", userName); params.add("password", token); params.add("queryName", "getUserByNameAuth"); try { if (StringUtils.isEmpty(userName) || StringUtils.isEmpty(token)) { throw new AccessDeniedException("User is unauthorized. Please check user name and password"); } Assert.notNull(rt, "Rest template can not be empty"); LOGGER.debug("Calling authentication service with URL {}, User {} and Parameters {} ", otfServiceUrl, userName); JsonNode obj = rt.postForObject(otfServiceUrl, params, JsonNode.class); LOGGER.debug("authentication service call successfully returned with {} ", obj); //populate user with other user details populateUser(user, obj); //now check if user has access to Refset app. params = new LinkedMultiValueMap<String, String>(); params.add("username", userName); params.add("queryName", "getUserApps"); LOGGER.debug("Calling autorization service with URL {}, User {} and Parameters {} ", otfServiceUrl, userName); JsonNode appJson = rt.postForObject(otfServiceUrl, params, JsonNode.class); LOGGER.debug("autorization service call successfully returned with {} ", appJson); JsonNode apps = appJson.get("apps"); Collection<RefsetRole> roles = new ArrayList<RefsetRole>(); for (JsonNode object : apps) { if (object != null && object.asText().equals(APP_NAME)) { RefsetRole role = new RefsetRole(); role.setAuthority(ROLE_USER); roles.add(role); break; } } user.setAuthorities(roles); if (isUserHasRole(user)) { String info = userName + ":" + token; Token key = service.allocateToken(info); user.setToken(key.getKey()); } } catch (Exception e) { LOGGER.error("Error during authentication for user:password - {} ", userName + ":" + token, e); throw new AccessDeniedException("User is unauthorized. Please check user name and password"); } return user; } /** * @param obj */ private User populateUser(User user, JsonNode obj) { //mandatory values JsonNode userJson = obj.get("user"); String id = userJson.findValue("name").asText(); String status = userJson.findValue("status").asText(); boolean authenticated = !StringUtils.isEmpty(status) && status.equalsIgnoreCase("enabled") ? true : false; user.setUsername(id); user.setPassword(userJson.findValue("token").asText()); user.setAuthenticated(authenticated); user.setEnabled(authenticated); //other details JsonNode email = userJson.findValue("email"); if(email != null) { user.setEmail(email.asText()); } JsonNode middleName = userJson.findValue("middleName"); if(middleName != null) { user.setMiddlename(middleName.asText()); } JsonNode givenName = userJson.findValue("givenName"); if(givenName != null) { user.setGivenname(givenName.asText()); } JsonNode surname = userJson.findValue("surname"); if(surname != null) { user.setSurname(surname.asText()); } return user; } /**TODO need * Gets a brand new user with default role assigned * @return */ public User getGuestUser() { Collection<RefsetRole> roles = new ArrayList<RefsetRole>(); User user = new User(); RefsetRole role = new RefsetRole(); role.setAuthority(ROLE_ANONYMOUS); roles.add(role); user.setAuthorities(roles); user.setUsername("guest"); user.setPassword("guest"); return user; } /** * Gets a minimal {@link Authentication} object from {@link Token}. * It is only possible if that Token exist. At the moment ROLE_USER is defaulted * @return */ protected Authentication getPrincipal(Token token) { String info = token.getExtendedInformation(); String [] details = StringUtils.split(info, ":"); User user = new User(); user.setPassword(details[1]); user.setUsername(details[0]); user.setAuthenticated(true); user.setEnabled(true); Collection<RefsetRole> roles = new ArrayList<RefsetRole>(); RefsetRole role = new RefsetRole(); role.setAuthority(ROLE_USER); roles.add(role); user.setAuthorities(roles); Authentication auth = new UsernamePasswordAuthenticationToken(user, details[1], roles); return auth; } private boolean isUserHasRole(User auth) { boolean isUserHasRole = false; if (auth!=null && auth.isAuthenticated()) { Collection<? extends GrantedAuthority> roles = auth.getAuthorities(); for (GrantedAuthority role : roles) { isUserHasRole = "ROLE_USER".equals(role.getAuthority()) ? true : false; if (isUserHasRole) { break; } } } return isUserHasRole; } /** * @param otfServiceUrl the otfServiceUrl to set */ public void setOtfServiceUrl(String otfServiceUrl) { this.otfServiceUrl = otfServiceUrl; } /** * @param rt the rt to set */ public void setRt(RestTemplate rt) { this.rt = rt; } /** * @param service the service to set */ public void setService(TokenService service) { this.service = service; } }