Fix token sessionId calculation
This commit is contained in:
parent
bf5d99fab1
commit
5e2a4a73b7
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.controller;
|
package org.thingsboard.server.controller;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -34,8 +33,8 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
|
||||||
import org.thingsboard.rule.engine.api.MailService;
|
import org.thingsboard.rule.engine.api.MailService;
|
||||||
|
import org.thingsboard.server.cache.limits.RateLimitService;
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.audit.ActionType;
|
import org.thingsboard.server.common.data.audit.ActionType;
|
||||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
||||||
@ -49,7 +48,6 @@ import org.thingsboard.server.common.data.security.model.JwtPair;
|
|||||||
import org.thingsboard.server.common.data.security.model.SecuritySettings;
|
import org.thingsboard.server.common.data.security.model.SecuritySettings;
|
||||||
import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
|
import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
|
||||||
import org.thingsboard.server.config.annotations.ApiOperation;
|
import org.thingsboard.server.config.annotations.ApiOperation;
|
||||||
import org.thingsboard.server.cache.limits.RateLimitService;
|
|
||||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||||
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
|
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
|
||||||
import org.thingsboard.server.service.security.model.ActivateUserRequest;
|
import org.thingsboard.server.service.security.model.ActivateUserRequest;
|
||||||
@ -105,8 +103,7 @@ public class AuthController extends BaseController {
|
|||||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||||
@RequestMapping(value = "/auth/changePassword", method = RequestMethod.POST)
|
@RequestMapping(value = "/auth/changePassword", method = RequestMethod.POST)
|
||||||
@ResponseStatus(value = HttpStatus.OK)
|
@ResponseStatus(value = HttpStatus.OK)
|
||||||
public ObjectNode changePassword(
|
public JwtPair changePassword(@Parameter(description = "Change Password Request")
|
||||||
@Parameter(description = "Change Password Request")
|
|
||||||
@RequestBody ChangePasswordRequest changePasswordRequest) throws ThingsboardException {
|
@RequestBody ChangePasswordRequest changePasswordRequest) throws ThingsboardException {
|
||||||
String currentPassword = changePasswordRequest.getCurrentPassword();
|
String currentPassword = changePasswordRequest.getCurrentPassword();
|
||||||
String newPassword = changePasswordRequest.getNewPassword();
|
String newPassword = changePasswordRequest.getNewPassword();
|
||||||
@ -123,10 +120,7 @@ public class AuthController extends BaseController {
|
|||||||
userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials);
|
userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials);
|
||||||
|
|
||||||
eventPublisher.publishEvent(new UserCredentialsInvalidationEvent(securityUser.getId()));
|
eventPublisher.publishEvent(new UserCredentialsInvalidationEvent(securityUser.getId()));
|
||||||
ObjectNode response = JacksonUtil.newObjectNode();
|
return tokenFactory.createTokenPair(securityUser);
|
||||||
response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken());
|
|
||||||
response.put("refreshToken", tokenFactory.createRefreshToken(securityUser).getToken());
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation(value = "Get the current User password policy (getUserPasswordPolicy)",
|
@ApiOperation(value = "Get the current User password policy (getUserPasswordPolicy)",
|
||||||
|
|||||||
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.security.model;
|
package org.thingsboard.server.service.security.model;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
@ -30,9 +32,12 @@ public class SecurityUser extends User {
|
|||||||
private static final long serialVersionUID = -797397440703066079L;
|
private static final long serialVersionUID = -797397440703066079L;
|
||||||
|
|
||||||
private Collection<GrantedAuthority> authorities;
|
private Collection<GrantedAuthority> authorities;
|
||||||
|
@Getter @Setter
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
@Getter @Setter
|
||||||
private UserPrincipal userPrincipal;
|
private UserPrincipal userPrincipal;
|
||||||
private String sessionId;
|
@Getter @Setter
|
||||||
|
private String sessionId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
public SecurityUser() {
|
public SecurityUser() {
|
||||||
super();
|
super();
|
||||||
@ -46,7 +51,6 @@ public class SecurityUser extends User {
|
|||||||
super(user);
|
super(user);
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
this.userPrincipal = userPrincipal;
|
this.userPrincipal = userPrincipal;
|
||||||
this.sessionId = UUID.randomUUID().toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<GrantedAuthority> getAuthorities() {
|
public Collection<GrantedAuthority> getAuthorities() {
|
||||||
@ -58,27 +62,4 @@ public class SecurityUser extends User {
|
|||||||
return authorities;
|
return authorities;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserPrincipal getUserPrincipal() {
|
|
||||||
return userPrincipal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserPrincipal(UserPrincipal userPrincipal) {
|
|
||||||
this.userPrincipal = userPrincipal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSessionId() {
|
|
||||||
return sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSessionId(String sessionId) {
|
|
||||||
this.sessionId = sessionId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -232,6 +232,7 @@ public class JwtTokenFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public JwtPair createTokenPair(SecurityUser securityUser) {
|
public JwtPair createTokenPair(SecurityUser securityUser) {
|
||||||
|
securityUser.setSessionId(UUID.randomUUID().toString());
|
||||||
JwtToken accessToken = createAccessJwtToken(securityUser);
|
JwtToken accessToken = createAccessJwtToken(securityUser);
|
||||||
JwtToken refreshToken = createRefreshToken(securityUser);
|
JwtToken refreshToken = createRefreshToken(securityUser);
|
||||||
return new JwtPair(accessToken.getToken(), refreshToken.getToken());
|
return new JwtPair(accessToken.getToken(), refreshToken.getToken());
|
||||||
|
|||||||
@ -73,16 +73,7 @@ public class JwtTokenFactoryTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateAndParseAccessJwtToken() {
|
public void testCreateAndParseAccessJwtToken() {
|
||||||
SecurityUser securityUser = new SecurityUser();
|
SecurityUser securityUser = createSecurityUser();
|
||||||
securityUser.setId(new UserId(UUID.randomUUID()));
|
|
||||||
securityUser.setEmail("tenant@thingsboard.org");
|
|
||||||
securityUser.setAuthority(Authority.TENANT_ADMIN);
|
|
||||||
securityUser.setTenantId(new TenantId(UUID.randomUUID()));
|
|
||||||
securityUser.setEnabled(true);
|
|
||||||
securityUser.setFirstName("A");
|
|
||||||
securityUser.setLastName("B");
|
|
||||||
securityUser.setUserPrincipal(new UserPrincipal(UserPrincipal.Type.USER_NAME, securityUser.getEmail()));
|
|
||||||
securityUser.setCustomerId(new CustomerId(UUID.randomUUID()));
|
|
||||||
|
|
||||||
testCreateAndParseAccessJwtToken(securityUser);
|
testCreateAndParseAccessJwtToken(securityUser);
|
||||||
|
|
||||||
@ -111,18 +102,12 @@ public class JwtTokenFactoryTest {
|
|||||||
assertThat(parsedSecurityUser.getCustomerId()).isEqualTo(securityUser.getCustomerId());
|
assertThat(parsedSecurityUser.getCustomerId()).isEqualTo(securityUser.getCustomerId());
|
||||||
assertThat(parsedSecurityUser.getFirstName()).isEqualTo(securityUser.getFirstName());
|
assertThat(parsedSecurityUser.getFirstName()).isEqualTo(securityUser.getFirstName());
|
||||||
assertThat(parsedSecurityUser.getLastName()).isEqualTo(securityUser.getLastName());
|
assertThat(parsedSecurityUser.getLastName()).isEqualTo(securityUser.getLastName());
|
||||||
|
assertThat(parsedSecurityUser.getSessionId()).isNotNull().isEqualTo(securityUser.getSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateAndParseRefreshJwtToken() {
|
public void testCreateAndParseRefreshJwtToken() {
|
||||||
SecurityUser securityUser = new SecurityUser();
|
SecurityUser securityUser = createSecurityUser();
|
||||||
securityUser.setId(new UserId(UUID.randomUUID()));
|
|
||||||
securityUser.setEmail("tenant@thingsboard.org");
|
|
||||||
securityUser.setAuthority(Authority.TENANT_ADMIN);
|
|
||||||
securityUser.setUserPrincipal(new UserPrincipal(UserPrincipal.Type.USER_NAME, securityUser.getEmail()));
|
|
||||||
securityUser.setEnabled(true);
|
|
||||||
securityUser.setTenantId(new TenantId(UUID.randomUUID()));
|
|
||||||
securityUser.setCustomerId(new CustomerId(UUID.randomUUID()));
|
|
||||||
|
|
||||||
JwtToken refreshToken = tokenFactory.createRefreshToken(securityUser);
|
JwtToken refreshToken = tokenFactory.createRefreshToken(securityUser);
|
||||||
checkExpirationTime(refreshToken, jwtSettings.getRefreshTokenExpTime());
|
checkExpirationTime(refreshToken, jwtSettings.getRefreshTokenExpTime());
|
||||||
@ -138,15 +123,7 @@ public class JwtTokenFactoryTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateAndParsePreVerificationJwtToken() {
|
public void testCreateAndParsePreVerificationJwtToken() {
|
||||||
SecurityUser securityUser = new SecurityUser();
|
SecurityUser securityUser = createSecurityUser();
|
||||||
securityUser.setId(new UserId(UUID.randomUUID()));
|
|
||||||
securityUser.setEmail("tenant@thingsboard.org");
|
|
||||||
securityUser.setAuthority(Authority.TENANT_ADMIN);
|
|
||||||
securityUser.setUserPrincipal(new UserPrincipal(UserPrincipal.Type.USER_NAME, securityUser.getEmail()));
|
|
||||||
securityUser.setEnabled(true);
|
|
||||||
securityUser.setTenantId(new TenantId(UUID.randomUUID()));
|
|
||||||
securityUser.setCustomerId(new CustomerId(UUID.randomUUID()));
|
|
||||||
|
|
||||||
int tokenLifetime = (int) TimeUnit.MINUTES.toSeconds(30);
|
int tokenLifetime = (int) TimeUnit.MINUTES.toSeconds(30);
|
||||||
JwtToken preVerificationToken = tokenFactory.createPreVerificationToken(securityUser, tokenLifetime);
|
JwtToken preVerificationToken = tokenFactory.createPreVerificationToken(securityUser, tokenLifetime);
|
||||||
checkExpirationTime(preVerificationToken, tokenLifetime);
|
checkExpirationTime(preVerificationToken, tokenLifetime);
|
||||||
@ -162,6 +139,34 @@ public class JwtTokenFactoryTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSessionId() {
|
||||||
|
SecurityUser securityUser = createSecurityUser();
|
||||||
|
String sessionId = securityUser.getSessionId();
|
||||||
|
|
||||||
|
String accessToken = tokenFactory.createAccessJwtToken(securityUser).getToken();
|
||||||
|
securityUser = tokenFactory.parseAccessJwtToken(accessToken);
|
||||||
|
assertThat(securityUser.getSessionId()).isNotNull().isEqualTo(sessionId);
|
||||||
|
|
||||||
|
String newAccessToken = tokenFactory.createTokenPair(securityUser).getToken();
|
||||||
|
securityUser = tokenFactory.parseAccessJwtToken(newAccessToken);
|
||||||
|
assertThat(securityUser.getSessionId()).isNotNull().isNotEqualTo(sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecurityUser createSecurityUser() {
|
||||||
|
SecurityUser securityUser = new SecurityUser();
|
||||||
|
securityUser.setId(new UserId(UUID.randomUUID()));
|
||||||
|
securityUser.setEmail("tenant@thingsboard.org");
|
||||||
|
securityUser.setAuthority(Authority.TENANT_ADMIN);
|
||||||
|
securityUser.setTenantId(new TenantId(UUID.randomUUID()));
|
||||||
|
securityUser.setEnabled(true);
|
||||||
|
securityUser.setFirstName("A");
|
||||||
|
securityUser.setLastName("B");
|
||||||
|
securityUser.setUserPrincipal(new UserPrincipal(UserPrincipal.Type.USER_NAME, securityUser.getEmail()));
|
||||||
|
securityUser.setCustomerId(new CustomerId(UUID.randomUUID()));
|
||||||
|
return securityUser;
|
||||||
|
}
|
||||||
|
|
||||||
private void mockJwtSettings(JwtSettings settings) {
|
private void mockJwtSettings(JwtSettings settings) {
|
||||||
AdminSettings adminJwtSettings = new AdminSettings();
|
AdminSettings adminJwtSettings = new AdminSettings();
|
||||||
adminJwtSettings.setJsonValue(JacksonUtil.valueToTree(settings));
|
adminJwtSettings.setJsonValue(JacksonUtil.valueToTree(settings));
|
||||||
|
|||||||
@ -18,17 +18,17 @@ package org.thingsboard.server.common.data;
|
|||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
import org.thingsboard.server.common.data.id.UUIDBased;
|
import org.thingsboard.server.common.data.id.UUIDBased;
|
||||||
import org.thingsboard.server.common.data.validation.NoXss;
|
import org.thingsboard.server.common.data.validation.NoXss;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,6 +64,23 @@ public abstract class BaseDataWithAdditionalInfo<I extends UUIDBased> extends Ba
|
|||||||
setJson(addInfo, json -> this.additionalInfo = json, bytes -> this.additionalInfoBytes = bytes);
|
setJson(addInfo, json -> this.additionalInfo = json, bytes -> this.additionalInfoBytes = bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAdditionalInfoField(String field, JsonNode value) {
|
||||||
|
JsonNode additionalInfo = getAdditionalInfo();
|
||||||
|
if (!(additionalInfo instanceof ObjectNode)) {
|
||||||
|
additionalInfo = mapper.createObjectNode();
|
||||||
|
}
|
||||||
|
((ObjectNode) additionalInfo).set(field, value);
|
||||||
|
setAdditionalInfo(additionalInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getAdditionalInfoField(String field, Function<JsonNode, T> mapper, T defaultValue) {
|
||||||
|
JsonNode additionalInfo = getAdditionalInfo();
|
||||||
|
if (additionalInfo != null && additionalInfo.has(field)) {
|
||||||
|
return mapper.apply(additionalInfo.get(field));
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.common.data.security.model;
|
package org.thingsboard.server.common.data.security.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@ -25,6 +26,7 @@ import java.io.Serializable;
|
|||||||
@Schema(description = "JWT Pair")
|
@Schema(description = "JWT Pair")
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
public class JwtPair implements Serializable {
|
public class JwtPair implements Serializable {
|
||||||
|
|
||||||
@Schema(description = "The JWT Access Token. Used to perform API calls.", example = "AAB254FF67D..")
|
@Schema(description = "The JWT Access Token. Used to perform API calls.", example = "AAB254FF67D..")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user