mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-31 20:51:26 +00:00
Fix: Improve error messages (#11244)
This commit is contained in:
parent
33c429f67c
commit
a4af11fba4
@ -62,6 +62,10 @@ public abstract class SecretsManager {
|
|||||||
return encryptOrDecryptPasswordFields(
|
return encryptOrDecryptPasswordFields(
|
||||||
newConnectionConfig, buildSecretId(true, serviceType.value(), connectionName), encrypt, true);
|
newConnectionConfig, buildSecretId(true, serviceType.value(), connectionName), encrypt, true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
String message = SecretsUtil.buildExceptionMessageConnection(e.getMessage(), connectionType, encrypt);
|
||||||
|
if (message != null) {
|
||||||
|
throw new InvalidServiceConnectionException(message);
|
||||||
|
}
|
||||||
throw InvalidServiceConnectionException.byMessage(
|
throw InvalidServiceConnectionException.byMessage(
|
||||||
connectionType, String.format("Failed to encrypt connection instance of %s", connectionType));
|
connectionType, String.format("Failed to encrypt connection instance of %s", connectionType));
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Collate
|
||||||
|
* 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.openmetadata.service.secrets;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class SecretsUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an error message when it is related to an Unrecognized field
|
||||||
|
*
|
||||||
|
* @param message the message to be formatted if the Unrecognized field is between quotes
|
||||||
|
* @param defaultMessage default message to be formatted if the Unrecognized field is not between quotes
|
||||||
|
* @param exceptionMessage the exception message
|
||||||
|
* @param type the type of error
|
||||||
|
* @return null if the message does not contain 'Unrecognized field' in the exception message
|
||||||
|
*/
|
||||||
|
public static String buildExceptionMessageUnrecognizedField(
|
||||||
|
String message, String defaultMessage, String exceptionMessage, String type) {
|
||||||
|
if (exceptionMessage != null && exceptionMessage.contains("Unrecognized field")) {
|
||||||
|
Pattern pattern = Pattern.compile("Unrecognized field \"(.*?)\"");
|
||||||
|
Matcher matcher = pattern.matcher(exceptionMessage);
|
||||||
|
if (matcher.find()) {
|
||||||
|
String fieldValue = matcher.group(1);
|
||||||
|
return String.format(message, type, fieldValue);
|
||||||
|
}
|
||||||
|
return String.format(defaultMessage, type);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildExceptionMessageConnection(
|
||||||
|
String exceptionMessage, String type, String firstAction, String secondAction, boolean isFirstAction) {
|
||||||
|
return buildExceptionMessageUnrecognizedField(
|
||||||
|
"Failed to "
|
||||||
|
+ (isFirstAction ? firstAction : secondAction)
|
||||||
|
+ " '%s' connection stored in DB due to an unrecognized field: '%s'",
|
||||||
|
"Failed to "
|
||||||
|
+ (isFirstAction ? firstAction : secondAction)
|
||||||
|
+ " '%s' connection stored in DB due to malformed connection object.",
|
||||||
|
exceptionMessage,
|
||||||
|
type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildExceptionMessageConnection(String exceptionMessage, String type, boolean encrypt) {
|
||||||
|
return buildExceptionMessageConnection(exceptionMessage, type, "encrypt", "decrypt", encrypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildExceptionMessageConnectionMask(String exceptionMessage, String type, boolean mask) {
|
||||||
|
return buildExceptionMessageConnection(exceptionMessage, type, "mask", "unmask", mask);
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ import org.openmetadata.schema.entity.services.ingestionPipelines.IngestionPipel
|
|||||||
import org.openmetadata.schema.entity.teams.AuthenticationMechanism;
|
import org.openmetadata.schema.entity.teams.AuthenticationMechanism;
|
||||||
import org.openmetadata.service.exception.EntityMaskException;
|
import org.openmetadata.service.exception.EntityMaskException;
|
||||||
import org.openmetadata.service.fernet.Fernet;
|
import org.openmetadata.service.fernet.Fernet;
|
||||||
|
import org.openmetadata.service.secrets.SecretsUtil;
|
||||||
import org.openmetadata.service.secrets.converter.ClassConverterFactory;
|
import org.openmetadata.service.secrets.converter.ClassConverterFactory;
|
||||||
import org.openmetadata.service.util.AuthenticationMechanismBuilder;
|
import org.openmetadata.service.util.AuthenticationMechanismBuilder;
|
||||||
import org.openmetadata.service.util.IngestionPipelineBuilder;
|
import org.openmetadata.service.util.IngestionPipelineBuilder;
|
||||||
@ -54,6 +55,10 @@ public class PasswordEntityMasker extends EntityMasker {
|
|||||||
maskPasswordFields(convertedConnectionConfig);
|
maskPasswordFields(convertedConnectionConfig);
|
||||||
return convertedConnectionConfig;
|
return convertedConnectionConfig;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
String message = SecretsUtil.buildExceptionMessageConnectionMask(e.getMessage(), connectionType, true);
|
||||||
|
if (message != null) {
|
||||||
|
throw new EntityMaskException(message);
|
||||||
|
}
|
||||||
throw new EntityMaskException(String.format("Failed to mask connection instance of %s", connectionType));
|
throw new EntityMaskException(String.format("Failed to mask connection instance of %s", connectionType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,6 +114,10 @@ public class PasswordEntityMasker extends EntityMasker {
|
|||||||
unmaskPasswordFields(toUnmaskConfig, NEW_KEY, passwordsMap);
|
unmaskPasswordFields(toUnmaskConfig, NEW_KEY, passwordsMap);
|
||||||
return toUnmaskConfig;
|
return toUnmaskConfig;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
String message = SecretsUtil.buildExceptionMessageConnectionMask(e.getMessage(), connectionType, false);
|
||||||
|
if (message != null) {
|
||||||
|
throw new EntityMaskException(message);
|
||||||
|
}
|
||||||
throw new EntityMaskException(String.format("Failed to unmask connection instance of %s", connectionType));
|
throw new EntityMaskException(String.format("Failed to unmask connection instance of %s", connectionType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ package org.openmetadata.service.secrets;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
@ -41,6 +42,7 @@ import org.openmetadata.schema.security.secrets.SecretsManagerProvider;
|
|||||||
import org.openmetadata.schema.services.connections.database.MysqlConnection;
|
import org.openmetadata.schema.services.connections.database.MysqlConnection;
|
||||||
import org.openmetadata.schema.services.connections.metadata.OpenMetadataConnection;
|
import org.openmetadata.schema.services.connections.metadata.OpenMetadataConnection;
|
||||||
import org.openmetadata.service.Entity;
|
import org.openmetadata.service.Entity;
|
||||||
|
import org.openmetadata.service.exception.InvalidServiceConnectionException;
|
||||||
import org.openmetadata.service.fernet.Fernet;
|
import org.openmetadata.service.fernet.Fernet;
|
||||||
import org.openmetadata.service.util.JsonUtils;
|
import org.openmetadata.service.util.JsonUtils;
|
||||||
|
|
||||||
@ -105,6 +107,34 @@ public abstract class ExternalSecretsManagerTest {
|
|||||||
testEncryptWorkflowObject(ENCRYPT);
|
testEncryptWorkflowObject(ENCRYPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testExceptionConnection() {
|
||||||
|
CreateDatabaseService.DatabaseServiceType databaseServiceType = CreateDatabaseService.DatabaseServiceType.Mysql;
|
||||||
|
String connectionName = "test";
|
||||||
|
Map<String, String> mysqlConnection = Map.of("password", "openmetadata-test", "username1", "openmetadata-test");
|
||||||
|
|
||||||
|
InvalidServiceConnectionException thrown =
|
||||||
|
Assertions.assertThrows(
|
||||||
|
InvalidServiceConnectionException.class,
|
||||||
|
() ->
|
||||||
|
secretsManager.encryptOrDecryptServiceConnectionConfig(
|
||||||
|
mysqlConnection, databaseServiceType.value(), connectionName, ServiceType.DATABASE, true));
|
||||||
|
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"Failed to encrypt 'Mysql' connection stored in DB due to an unrecognized field: 'username1'",
|
||||||
|
thrown.getMessage());
|
||||||
|
thrown =
|
||||||
|
Assertions.assertThrows(
|
||||||
|
InvalidServiceConnectionException.class,
|
||||||
|
() ->
|
||||||
|
secretsManager.encryptOrDecryptServiceConnectionConfig(
|
||||||
|
mysqlConnection, databaseServiceType.value(), connectionName, ServiceType.DATABASE, false));
|
||||||
|
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"Failed to decrypt 'Mysql' connection stored in DB due to an unrecognized field: 'username1'",
|
||||||
|
thrown.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testReturnsExpectedSecretManagerProvider() {
|
void testReturnsExpectedSecretManagerProvider() {
|
||||||
assertEquals(expectedSecretManagerProvider(), secretsManager.getSecretsManagerProvider());
|
assertEquals(expectedSecretManagerProvider(), secretsManager.getSecretsManagerProvider());
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
package org.openmetadata.service.secrets.masker;
|
package org.openmetadata.service.secrets.masker;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.openmetadata.schema.entity.services.ServiceType;
|
||||||
|
import org.openmetadata.schema.services.connections.database.MysqlConnection;
|
||||||
|
import org.openmetadata.service.exception.EntityMaskException;
|
||||||
|
|
||||||
public class PasswordEntityMaskerTest extends TestEntityMasker {
|
public class PasswordEntityMaskerTest extends TestEntityMasker {
|
||||||
public PasswordEntityMaskerTest() {
|
public PasswordEntityMaskerTest() {
|
||||||
CONFIG.setMaskPasswordsAPI(true);
|
CONFIG.setMaskPasswordsAPI(true);
|
||||||
@ -9,4 +16,35 @@ public class PasswordEntityMaskerTest extends TestEntityMasker {
|
|||||||
protected String getMaskedPassword() {
|
protected String getMaskedPassword() {
|
||||||
return PasswordEntityMasker.PASSWORD_MASK;
|
return PasswordEntityMasker.PASSWORD_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testExceptionConnection() {
|
||||||
|
Map<String, String> mysqlConnectionObject =
|
||||||
|
Map.of("password", "openmetadata-test", "username1", "openmetadata-test");
|
||||||
|
|
||||||
|
EntityMaskException thrown =
|
||||||
|
Assertions.assertThrows(
|
||||||
|
EntityMaskException.class,
|
||||||
|
() -> {
|
||||||
|
EntityMaskerFactory.createEntityMasker(CONFIG)
|
||||||
|
.maskServiceConnectionConfig(mysqlConnectionObject, "Mysql", ServiceType.DATABASE);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"Failed to mask 'Mysql' connection stored in DB due to an unrecognized field: 'username1'",
|
||||||
|
thrown.getMessage());
|
||||||
|
|
||||||
|
thrown =
|
||||||
|
Assertions.assertThrows(
|
||||||
|
EntityMaskException.class,
|
||||||
|
() -> {
|
||||||
|
EntityMaskerFactory.createEntityMasker(CONFIG)
|
||||||
|
.unmaskServiceConnectionConfig(
|
||||||
|
mysqlConnectionObject, new MysqlConnection(), "Mysql", ServiceType.DATABASE);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"Failed to unmask 'Mysql' connection stored in DB due to an unrecognized field: 'username1'",
|
||||||
|
thrown.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Metadata
|
# Metadata
|
||||||
|
|
||||||
DatabaseService Metadata Pipeline Configuration.
|
Database Service Metadata Pipeline Configuration.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
@ -1382,4 +1382,4 @@
|
|||||||
"update-entity-success": "{{entity}} updated successfully.",
|
"update-entity-success": "{{entity}} updated successfully.",
|
||||||
"you-have-not-action-anything-yet": "You have not {{action}} anything yet."
|
"you-have-not-action-anything-yet": "You have not {{action}} anything yet."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1382,4 +1382,4 @@
|
|||||||
"update-entity-success": "{{entity}} actualizado exitosamente.",
|
"update-entity-success": "{{entity}} actualizado exitosamente.",
|
||||||
"you-have-not-action-anything-yet": "Todavía no has {{action}} nada."
|
"you-have-not-action-anything-yet": "Todavía no has {{action}} nada."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1382,4 +1382,4 @@
|
|||||||
"update-entity-success": "{{entity}} mis à jour avec succès.",
|
"update-entity-success": "{{entity}} mis à jour avec succès.",
|
||||||
"you-have-not-action-anything-yet": "Vous n'avez encore rien {{action}}."
|
"you-have-not-action-anything-yet": "Vous n'avez encore rien {{action}}."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1382,4 +1382,4 @@
|
|||||||
"update-entity-success": "{{entity}}は正常に更新されました。",
|
"update-entity-success": "{{entity}}は正常に更新されました。",
|
||||||
"you-have-not-action-anything-yet": "あなたが{{action}}のデータはありません。"
|
"you-have-not-action-anything-yet": "あなたが{{action}}のデータはありません。"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1382,4 +1382,4 @@
|
|||||||
"update-entity-success": "{{entity}} atualizado com sucesso.",
|
"update-entity-success": "{{entity}} atualizado com sucesso.",
|
||||||
"you-have-not-action-anything-yet": "Você ainda não {{action}} nada."
|
"you-have-not-action-anything-yet": "Você ainda não {{action}} nada."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1382,4 +1382,4 @@
|
|||||||
"update-entity-success": "{{entity}}已成功更新",
|
"update-entity-success": "{{entity}}已成功更新",
|
||||||
"you-have-not-action-anything-yet": "您还没有{{action}}任何内容"
|
"you-have-not-action-anything-yet": "您还没有{{action}}任何内容"
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user