/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * 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.constellation.services.web.controller; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.WebApplicationException; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.mail.EmailException; import org.constellation.admin.mail.MailService; import org.constellation.auth.transfer.TokenTransfer; import org.constellation.database.api.jooq.tables.pojos.CstlUser; import org.constellation.database.api.repository.UserRepository; import org.constellation.services.component.TokenService; import org.geotoolkit.util.StringUtilities; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Profile; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Controller; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.google.common.base.Optional; import java.text.MessageFormat; import java.util.Collections; import java.util.ResourceBundle; @Controller @Profile("standard") public class AuthController { static class Login { private String username; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } private String password; } static class ForgotPassword { private String email; public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } static class ResetPassword{ private String password; private String uuid; public String getUuid() { return uuid; } public void setUuid(String uuid) { this.uuid = uuid; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } @Autowired private TokenService tokenService; @Autowired private UserDetailsService userService; @Autowired private UserRepository userRepository; @Autowired private MailService mailService; @Autowired @Qualifier("authenticationManager") private AuthenticationManager authManager; /** * Authenticates a user and creates an authentication token. * * @param login pojo that contains the name and the password of the user. * @return A transfer containing the authentication token. */ @RequestMapping(value="/login", method=RequestMethod.POST) public ResponseEntity<TokenTransfer> login(HttpServletRequest request, @RequestBody Login login) { if (authManager == null) { return new ResponseEntity<TokenTransfer>(HttpStatus.INTERNAL_SERVER_ERROR); } UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword()); try { Authentication authentication = this.authManager.authenticate(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (BadCredentialsException e) { return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } catch (DisabledException exception) { return new ResponseEntity<>(HttpStatus.FORBIDDEN); } catch (Exception ex) { LoggerFactory.getLogger(AuthController.class).warn(ex.getMessage(), ex); } /* * Reload user as password of authentication principal will be null * after authorization and password is needed for token generation */ UserDetails userDetails = this.userService.loadUserByUsername(login.getUsername()); String createToken = tokenService.createToken(userDetails.getUsername()); Optional<CstlUser> findOne = userRepository.findOne(userDetails.getUsername()); int id = findOne.get().getId(); return new ResponseEntity<TokenTransfer>(new TokenTransfer(createToken, id), HttpStatus.OK); } @RequestMapping(value="/forgotPassword", method=RequestMethod.POST) @Transactional(rollbackFor = Exception.class) public ResponseEntity forgotPassword(HttpServletRequest request, @RequestBody final ForgotPassword forgotPassword) throws EmailException { final String email = forgotPassword.getEmail(); String uuid = DigestUtils.sha256Hex(email + System.currentTimeMillis()); Optional<CstlUser> userOptional = userRepository.findByEmail(email); if(userOptional.isPresent()){ CstlUser user = userOptional.get(); user.setForgotPasswordUuid(uuid); userRepository.update(user); String baseUrl = "http://" + request.getHeader("host") + request.getContextPath(); String resetPasswordUrl = baseUrl + "/reset-password.html?uuid=" + uuid; ResourceBundle bundle = ResourceBundle.getBundle("org/constellation/admin/mail/mail", LocaleUtils.toLocale(user.getLocale())); Object[] args = {user.getFirstname(), user.getLastname(), resetPasswordUrl}; mailService.send(bundle.getString("account.password.reset.subject"), MessageFormat.format(bundle.getString("account.password.reset.body"), args), Collections.singletonList(email)); return new ResponseEntity(HttpStatus.OK); } return new ResponseEntity(HttpStatus.BAD_REQUEST); } @RequestMapping(value="/resetPassword", method=RequestMethod.POST) @Transactional(rollbackFor = Exception.class) public ResponseEntity resetPassword(@RequestBody final ResetPassword resetPassword){ String newPassword = resetPassword.getPassword(), uuid = resetPassword.getUuid(); if(newPassword != null && uuid != null && !newPassword.isEmpty() && !uuid.isEmpty()){ Optional<CstlUser> userOptional = userRepository.findByForgotPasswordUuid(uuid); if (userOptional.isPresent()){ CstlUser cstlUser = userOptional.get(); cstlUser.setPassword(StringUtilities.MD5encode(newPassword)); cstlUser.setForgotPasswordUuid(null); userRepository.update(cstlUser); return new ResponseEntity(HttpStatus.OK); } } return new ResponseEntity(HttpStatus.BAD_REQUEST); } public static UserDetails extractUserDetail() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Object principal = authentication.getPrincipal(); if (principal instanceof String && ((String) principal).equals("anonymousUser")) { throw new WebApplicationException(401); } UserDetails userDetails = (UserDetails) principal; return userDetails; } }