/**
* Copyright © 2016 The Thingsboard Authors
*
* 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.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.mail.MailService;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.JwtToken;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.net.URISyntaxException;
@RestController
@RequestMapping("/api")
@Slf4j
public class AuthController extends BaseController {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
private JwtTokenFactory tokenFactory;
@Autowired
private RefreshTokenRepository refreshTokenRepository;
@Autowired
private UserService userService;
@Autowired
private MailService mailService;
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = "/auth/user", method = RequestMethod.GET)
public @ResponseBody User getUser() throws ThingsboardException {
try {
SecurityUser securityUser = getCurrentUser();
return userService.findUserById(securityUser.getId());
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = "/auth/changePassword", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void changePassword (
@RequestParam(value = "currentPassword") String currentPassword,
@RequestParam(value = "newPassword") String newPassword) throws ThingsboardException {
try {
SecurityUser securityUser = getCurrentUser();
UserCredentials userCredentials = userService.findUserCredentialsByUserId(securityUser.getId());
if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) {
throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
userCredentials.setPassword(passwordEncoder.encode(newPassword));
userService.saveUserCredentials(userCredentials);
} catch (Exception e) {
throw handleException(e);
}
}
@RequestMapping(value = "/noauth/activate", params = { "activateToken" }, method = RequestMethod.GET)
public ResponseEntity<String> checkActivateToken(
@RequestParam(value = "activateToken") String activateToken) {
HttpHeaders headers = new HttpHeaders();
HttpStatus responseStatus;
UserCredentials userCredentials = userService.findUserCredentialsByActivateToken(activateToken);
if (userCredentials != null) {
String createPasswordURI = "/login/createPassword";
try {
URI location = new URI(createPasswordURI + "?activateToken=" + activateToken);
headers.setLocation(location);
responseStatus = HttpStatus.PERMANENT_REDIRECT;
} catch (URISyntaxException e) {
log.error("Unable to create URI with address [{}]", createPasswordURI);
responseStatus = HttpStatus.BAD_REQUEST;
}
} else {
responseStatus = HttpStatus.CONFLICT;
}
return new ResponseEntity<>(headers, responseStatus);
}
@RequestMapping(value = "/noauth/resetPasswordByEmail", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void requestResetPasswordByEmail (
@RequestParam(value = "email") String email,
HttpServletRequest request) throws ThingsboardException {
try {
UserCredentials userCredentials = userService.requestPasswordReset(email);
String baseUrl = String.format("%s://%s:%d",
request.getScheme(),
request.getServerName(),
request.getServerPort());
String resetPasswordUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl,
userCredentials.getResetToken());
mailService.sendResetPasswordEmail(resetPasswordUrl, email);
} catch (Exception e) {
throw handleException(e);
}
}
@RequestMapping(value = "/noauth/resetPassword", params = { "resetToken" }, method = RequestMethod.GET)
public ResponseEntity<String> checkResetToken(
@RequestParam(value = "resetToken") String resetToken) {
HttpHeaders headers = new HttpHeaders();
HttpStatus responseStatus;
String resetPasswordURI = "/login/resetPassword";
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(resetToken);
if (userCredentials != null) {
try {
URI location = new URI(resetPasswordURI + "?resetToken=" + resetToken);
headers.setLocation(location);
responseStatus = HttpStatus.PERMANENT_REDIRECT;
} catch (URISyntaxException e) {
log.error("Unable to create URI with address [{}]", resetPasswordURI);
responseStatus = HttpStatus.BAD_REQUEST;
}
} else {
responseStatus = HttpStatus.CONFLICT;
}
return new ResponseEntity<>(headers, responseStatus);
}
@RequestMapping(value = "/noauth/activate", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public JsonNode activateUser(
@RequestParam(value = "activateToken") String activateToken,
@RequestParam(value = "password") String password,
HttpServletRequest request) throws ThingsboardException {
try {
String encodedPassword = passwordEncoder.encode(password);
UserCredentials credentials = userService.activateUserCredentials(activateToken, encodedPassword);
User user = userService.findUserById(credentials.getUserId());
SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled());
String baseUrl = String.format("%s://%s:%d",
request.getScheme(),
request.getServerName(),
request.getServerPort());
String loginUrl = String.format("%s/login", baseUrl);
String email = user.getEmail();
mailService.sendAccountActivatedEmail(loginUrl, email);
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode tokenObject = objectMapper.createObjectNode();
tokenObject.put("token", accessToken.getToken());
tokenObject.put("refreshToken", refreshToken.getToken());
return tokenObject;
} catch (Exception e) {
throw handleException(e);
}
}
@RequestMapping(value = "/noauth/resetPassword", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public JsonNode resetPassword(
@RequestParam(value = "resetToken") String resetToken,
@RequestParam(value = "password") String password,
HttpServletRequest request) throws ThingsboardException {
try {
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(resetToken);
if (userCredentials != null) {
String encodedPassword = passwordEncoder.encode(password);
userCredentials.setPassword(encodedPassword);
userCredentials.setResetToken(null);
userCredentials = userService.saveUserCredentials(userCredentials);
User user = userService.findUserById(userCredentials.getUserId());
SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled());
String baseUrl = String.format("%s://%s:%d",
request.getScheme(),
request.getServerName(),
request.getServerPort());
String loginUrl = String.format("%s/login", baseUrl);
String email = user.getEmail();
mailService.sendPasswordWasResetEmail(loginUrl, email);
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode tokenObject = objectMapper.createObjectNode();
tokenObject.put("token", accessToken.getToken());
tokenObject.put("refreshToken", refreshToken.getToken());
return tokenObject;
} else {
throw new ThingsboardException("Invalid reset token!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
} catch (Exception e) {
throw handleException(e);
}
}
}