Added generation an audit log for oauth2 login and with which provider was done an authorization

This commit is contained in:
oyurov 2022-10-16 15:50:42 +02:00
parent d7aa2e5660
commit 126d7215c5
9 changed files with 118 additions and 89 deletions

View File

@ -59,7 +59,6 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.UserPrincipal;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import org.thingsboard.server.service.security.system.SystemSecurityService; import org.thingsboard.server.service.security.system.SystemSecurityService;
import ua_parser.Client;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.net.URI; import java.net.URI;
@ -324,49 +323,7 @@ public class AuthController extends BaseController {
private void logLogoutAction(HttpServletRequest request) throws ThingsboardException { private void logLogoutAction(HttpServletRequest request) throws ThingsboardException {
try { try {
SecurityUser user = getCurrentUser(); systemSecurityService.logLoginAction(getCurrentUser(), new RestAuthenticationDetails(request), ActionType.LOGOUT, null, "REST");
RestAuthenticationDetails details = new RestAuthenticationDetails(request);
String clientAddress = details.getClientAddress();
String browser = "Unknown";
String os = "Unknown";
String device = "Unknown";
if (details.getUserAgent() != null) {
Client userAgent = details.getUserAgent();
if (userAgent.userAgent != null) {
browser = userAgent.userAgent.family;
if (userAgent.userAgent.major != null) {
browser += " " + userAgent.userAgent.major;
if (userAgent.userAgent.minor != null) {
browser += "." + userAgent.userAgent.minor;
if (userAgent.userAgent.patch != null) {
browser += "." + userAgent.userAgent.patch;
}
}
}
}
if (userAgent.os != null) {
os = userAgent.os.family;
if (userAgent.os.major != null) {
os += " " + userAgent.os.major;
if (userAgent.os.minor != null) {
os += "." + userAgent.os.minor;
if (userAgent.os.patch != null) {
os += "." + userAgent.os.patch;
if (userAgent.os.patchMinor != null) {
os += "." + userAgent.os.patchMinor;
}
}
}
}
}
if (userAgent.device != null) {
device = userAgent.device.family;
}
}
auditLogService.logEntityAction(
user.getTenantId(), user.getCustomerId(), user.getId(),
user.getName(), user.getId(), null, ActionType.LOGOUT, null, clientAddress, browser, os, device);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

View File

@ -58,6 +58,8 @@ import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
@RequiredArgsConstructor @RequiredArgsConstructor
public class TwoFactorAuthController extends BaseController { public class TwoFactorAuthController extends BaseController {
private static final String TwoFA_PROVIDER = "2FA ";
private final TwoFactorAuthService twoFactorAuthService; private final TwoFactorAuthService twoFactorAuthService;
private final TwoFaConfigManager twoFaConfigManager; private final TwoFaConfigManager twoFaConfigManager;
private final JwtTokenFactory tokenFactory; private final JwtTokenFactory tokenFactory;
@ -92,12 +94,12 @@ public class TwoFactorAuthController extends BaseController {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
boolean verificationSuccess = twoFactorAuthService.checkVerificationCode(user, providerType, verificationCode, true); boolean verificationSuccess = twoFactorAuthService.checkVerificationCode(user, providerType, verificationCode, true);
if (verificationSuccess) { if (verificationSuccess) {
systemSecurityService.logLoginAction(user, new RestAuthenticationDetails(servletRequest), ActionType.LOGIN, null); systemSecurityService.logLoginAction(user, new RestAuthenticationDetails(servletRequest), ActionType.LOGIN, null, TwoFA_PROVIDER + providerType);
user = new SecurityUser(userService.findUserById(user.getTenantId(), user.getId()), true, user.getUserPrincipal()); user = new SecurityUser(userService.findUserById(user.getTenantId(), user.getId()), true, user.getUserPrincipal());
return tokenFactory.createTokenPair(user); return tokenFactory.createTokenPair(user);
} else { } else {
ThingsboardException error = new ThingsboardException("Verification code is incorrect", ThingsboardErrorCode.BAD_REQUEST_PARAMS); ThingsboardException error = new ThingsboardException("Verification code is incorrect", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
systemSecurityService.logLoginAction(user, new RestAuthenticationDetails(servletRequest), ActionType.LOGIN, error); systemSecurityService.logLoginAction(user, new RestAuthenticationDetails(servletRequest), ActionType.LOGIN, error, TwoFA_PROVIDER + providerType);
throw error; throw error;
} }
} }

View File

@ -25,6 +25,7 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequ
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.security.model.JwtToken;
import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import org.thingsboard.server.service.security.system.SystemSecurityService; import org.thingsboard.server.service.security.system.SystemSecurityService;
@ -102,6 +104,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
clearAuthenticationAttributes(request, response); clearAuthenticationAttributes(request, response);
getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken()); getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken());
systemSecurityService.logLoginAction(securityUser, new RestAuthenticationDetails(request), ActionType.LOGIN, null, "OAUTH2: " + registration.getName());
} catch (Exception e) { } catch (Exception e) {
log.debug("Error occurred during processing authentication success result. " + log.debug("Error occurred during processing authentication success result. " +
"request [{}], response [{}], authentication [{}]", request, response, authentication, e); "request [{}], response [{}], authentication [{}]", request, response, authentication, e);

View File

@ -53,6 +53,8 @@ import java.util.UUID;
@TbCoreComponent @TbCoreComponent
public class RestAuthenticationProvider implements AuthenticationProvider { public class RestAuthenticationProvider implements AuthenticationProvider {
private static final String REST_PROVIDER = "REST";
private final SystemSecurityService systemSecurityService; private final SystemSecurityService systemSecurityService;
private final UserService userService; private final UserService userService;
private final CustomerService customerService; private final CustomerService customerService;
@ -87,7 +89,7 @@ public class RestAuthenticationProvider implements AuthenticationProvider {
if (twoFactorAuthService.isTwoFaEnabled(securityUser.getTenantId(), securityUser.getId())) { if (twoFactorAuthService.isTwoFaEnabled(securityUser.getTenantId(), securityUser.getId())) {
return new MfaAuthenticationToken(securityUser); return new MfaAuthenticationToken(securityUser);
} else { } else {
systemSecurityService.logLoginAction(securityUser, authentication.getDetails(), ActionType.LOGIN, null); systemSecurityService.logLoginAction(securityUser, authentication.getDetails(), ActionType.LOGIN, null, REST_PROVIDER);
} }
} else { } else {
String publicId = userPrincipal.getValue(); String publicId = userPrincipal.getValue();
@ -113,7 +115,7 @@ public class RestAuthenticationProvider implements AuthenticationProvider {
try { try {
systemSecurityService.validateUserCredentials(user.getTenantId(), userCredentials, username, password); systemSecurityService.validateUserCredentials(user.getTenantId(), userCredentials, username, password);
} catch (LockedException e) { } catch (LockedException e) {
systemSecurityService.logLoginAction(user, authentication.getDetails(), ActionType.LOCKOUT, null); systemSecurityService.logLoginAction(user, authentication.getDetails(), ActionType.LOCKOUT, null, REST_PROVIDER);
throw e; throw e;
} }
@ -122,7 +124,7 @@ public class RestAuthenticationProvider implements AuthenticationProvider {
return new SecurityUser(user, userCredentials.isEnabled(), userPrincipal); return new SecurityUser(user, userCredentials.isEnabled(), userPrincipal);
} catch (Exception e) { } catch (Exception e) {
systemSecurityService.logLoginAction(user, authentication.getDetails(), ActionType.LOGIN, e); systemSecurityService.logLoginAction(user, authentication.getDetails(), ActionType.LOGIN, e, REST_PROVIDER);
throw e; throw e;
} }
} }

View File

@ -59,8 +59,9 @@ 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.exception.UserPasswordExpiredException; import org.thingsboard.server.service.security.exception.UserPasswordExpiredException;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.utils.AuthorizationDetails;
import org.thingsboard.server.utils.MiscUtils; import org.thingsboard.server.utils.MiscUtils;
import ua_parser.Client; import org.thingsboard.server.utils.RestAuthenticationDetailsUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -232,7 +233,8 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
JsonNode additionalInfo = user.getAdditionalInfo(); JsonNode additionalInfo = user.getAdditionalInfo();
if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) { if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) {
JsonNode userPasswordHistoryJson = additionalInfo.get(UserServiceImpl.USER_PASSWORD_HISTORY); JsonNode userPasswordHistoryJson = additionalInfo.get(UserServiceImpl.USER_PASSWORD_HISTORY);
Map<String, String> userPasswordHistoryMap = JacksonUtil.convertValue(userPasswordHistoryJson, new TypeReference<>() {}); Map<String, String> userPasswordHistoryMap = JacksonUtil.convertValue(userPasswordHistoryJson, new TypeReference<>() {
});
for (Map.Entry<String, String> entry : userPasswordHistoryMap.entrySet()) { for (Map.Entry<String, String> entry : userPasswordHistoryMap.entrySet()) {
if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) { if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) {
throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days"); throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days");
@ -262,54 +264,24 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
} }
@Override @Override
public void logLoginAction(User user, Object authenticationDetails, ActionType actionType, Exception e) { public void logLoginAction(User user, Object authenticationDetails, ActionType actionType, Exception e, String provider) {
String clientAddress = "Unknown"; String clientAddress = "Unknown";
String browser = "Unknown"; String browser = "Unknown";
String os = "Unknown"; String os = "Unknown";
String device = "Unknown"; String device = "Unknown";
if (authenticationDetails instanceof RestAuthenticationDetails) { if (authenticationDetails instanceof RestAuthenticationDetails) {
RestAuthenticationDetails details = (RestAuthenticationDetails) authenticationDetails; AuthorizationDetails details = RestAuthenticationDetailsUtils.getRestAuthenticationDetails((RestAuthenticationDetails) authenticationDetails);
clientAddress = details.getClientAddress(); clientAddress = details.getClientAddress();
if (details.getUserAgent() != null) { browser = details.getBrowser();
Client userAgent = details.getUserAgent(); os = details.getOs();
if (userAgent.userAgent != null) { device = details.getDevice();
browser = userAgent.userAgent.family;
if (userAgent.userAgent.major != null) {
browser += " " + userAgent.userAgent.major;
if (userAgent.userAgent.minor != null) {
browser += "." + userAgent.userAgent.minor;
if (userAgent.userAgent.patch != null) {
browser += "." + userAgent.userAgent.patch;
}
}
}
}
if (userAgent.os != null) {
os = userAgent.os.family;
if (userAgent.os.major != null) {
os += " " + userAgent.os.major;
if (userAgent.os.minor != null) {
os += "." + userAgent.os.minor;
if (userAgent.os.patch != null) {
os += "." + userAgent.os.patch;
if (userAgent.os.patchMinor != null) {
os += "." + userAgent.os.patchMinor;
}
}
}
}
}
if (userAgent.device != null) {
device = userAgent.device.family;
}
}
} }
if (actionType == ActionType.LOGIN && e == null) { if (actionType == ActionType.LOGIN && e == null) {
userService.setLastLoginTs(user.getTenantId(), user.getId()); userService.setLastLoginTs(user.getTenantId(), user.getId());
} }
auditLogService.logEntityAction( auditLogService.logEntityAction(
user.getTenantId(), user.getCustomerId(), user.getId(), user.getTenantId(), user.getCustomerId(), user.getId(),
user.getName(), user.getId(), null, actionType, e, clientAddress, browser, os, device); user.getName(), user.getId(), null, actionType, e, clientAddress, browser, os, device, provider);
} }
private static boolean isPositiveInteger(Integer val) { private static boolean isPositiveInteger(Integer val) {

View File

@ -42,6 +42,6 @@ public interface SystemSecurityService {
String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest); String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest);
void logLoginAction(User user, Object authenticationDetails, ActionType actionType, Exception e); void logLoginAction(User user, Object authenticationDetails, ActionType actionType, Exception e, String provider);
} }

View File

@ -0,0 +1,28 @@
/**
* Copyright © 2016-2022 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.utils;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class AuthorizationDetails {
private String clientAddress;
private String browser;
private String os;
private String device;
}

View File

@ -0,0 +1,63 @@
/**
* Copyright © 2016-2022 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.utils;
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
import ua_parser.Client;
public class RestAuthenticationDetailsUtils {
public static AuthorizationDetails getRestAuthenticationDetails(RestAuthenticationDetails details) {
String clientAddress = details.getClientAddress();
String browser = "Unknown";
String os = "Unknown";
String device = "Unknown";
if (details.getUserAgent() != null) {
Client userAgent = details.getUserAgent();
if (userAgent.userAgent != null) {
browser = userAgent.userAgent.family;
if (userAgent.userAgent.major != null) {
browser += " " + userAgent.userAgent.major;
if (userAgent.userAgent.minor != null) {
browser += "." + userAgent.userAgent.minor;
if (userAgent.userAgent.patch != null) {
browser += "." + userAgent.userAgent.patch;
}
}
}
}
if (userAgent.os != null) {
os = userAgent.os.family;
if (userAgent.os.major != null) {
os += " " + userAgent.os.major;
if (userAgent.os.minor != null) {
os += "." + userAgent.os.minor;
if (userAgent.os.patch != null) {
os += "." + userAgent.os.patch;
if (userAgent.os.patchMinor != null) {
os += "." + userAgent.os.patchMinor;
}
}
}
}
}
if (userAgent.device != null) {
device = userAgent.device.family;
}
}
return new AuthorizationDetails(clientAddress, browser, os, device);
}
}

View File

@ -257,10 +257,12 @@ public class AuditLogServiceImpl implements AuditLogService {
String browser = extractParameter(String.class, 1, additionalInfo); String browser = extractParameter(String.class, 1, additionalInfo);
String os = extractParameter(String.class, 2, additionalInfo); String os = extractParameter(String.class, 2, additionalInfo);
String device = extractParameter(String.class, 3, additionalInfo); String device = extractParameter(String.class, 3, additionalInfo);
String provider = extractParameter(String.class, 4, additionalInfo);
actionData.put("clientAddress", clientAddress); actionData.put("clientAddress", clientAddress);
actionData.put("browser", browser); actionData.put("browser", browser);
actionData.put("os", os); actionData.put("os", os);
actionData.put("device", device); actionData.put("device", device);
actionData.put("provider", provider);
break; break;
case PROVISION_SUCCESS: case PROVISION_SUCCESS:
case PROVISION_FAILURE: case PROVISION_FAILURE: