[OMOperations] Add Reset Password option (#19253)

* Add Reset Password option

* Add Email option

* Add option to set initial password in yaml
This commit is contained in:
Mohit Yadav 2025-01-08 11:51:38 +05:30 committed by GitHub
parent 26bfde8f9c
commit dba3e58e4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 81 additions and 14 deletions

View File

@ -4,7 +4,9 @@ import static org.flywaydb.core.internal.info.MigrationInfoDumper.dumpToAsciiTab
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty; import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
import static org.openmetadata.service.Entity.FIELD_OWNERS; import static org.openmetadata.service.Entity.FIELD_OWNERS;
import static org.openmetadata.service.formatter.decorators.MessageDecorator.getDateStringEpochMilli; import static org.openmetadata.service.formatter.decorators.MessageDecorator.getDateStringEpochMilli;
import static org.openmetadata.service.jdbi3.UserRepository.AUTH_MECHANISM_FIELD;
import static org.openmetadata.service.util.AsciiTable.printOpenMetadataText; import static org.openmetadata.service.util.AsciiTable.printOpenMetadataText;
import static org.openmetadata.service.util.UserUtil.updateUserWithHashedPwd;
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
@ -23,6 +25,7 @@ import java.io.File;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -41,6 +44,8 @@ import org.openmetadata.schema.ServiceEntityInterface;
import org.openmetadata.schema.entity.app.App; import org.openmetadata.schema.entity.app.App;
import org.openmetadata.schema.entity.app.AppRunRecord; import org.openmetadata.schema.entity.app.AppRunRecord;
import org.openmetadata.schema.entity.services.ingestionPipelines.IngestionPipeline; import org.openmetadata.schema.entity.services.ingestionPipelines.IngestionPipeline;
import org.openmetadata.schema.entity.teams.User;
import org.openmetadata.schema.services.connections.metadata.AuthProvider;
import org.openmetadata.schema.settings.Settings; import org.openmetadata.schema.settings.Settings;
import org.openmetadata.schema.settings.SettingsType; import org.openmetadata.schema.settings.SettingsType;
import org.openmetadata.schema.system.EventPublisherJob; import org.openmetadata.schema.system.EventPublisherJob;
@ -60,6 +65,7 @@ import org.openmetadata.service.jdbi3.IngestionPipelineRepository;
import org.openmetadata.service.jdbi3.ListFilter; import org.openmetadata.service.jdbi3.ListFilter;
import org.openmetadata.service.jdbi3.MigrationDAO; import org.openmetadata.service.jdbi3.MigrationDAO;
import org.openmetadata.service.jdbi3.SystemRepository; import org.openmetadata.service.jdbi3.SystemRepository;
import org.openmetadata.service.jdbi3.UserRepository;
import org.openmetadata.service.jdbi3.locator.ConnectionType; import org.openmetadata.service.jdbi3.locator.ConnectionType;
import org.openmetadata.service.migration.api.MigrationWorkflow; import org.openmetadata.service.migration.api.MigrationWorkflow;
import org.openmetadata.service.resources.CollectionRegistry; import org.openmetadata.service.resources.CollectionRegistry;
@ -166,13 +172,12 @@ public class OpenMetadataOperations implements Callable<Integer> {
public Integer syncEmailFromEnv() { public Integer syncEmailFromEnv() {
try { try {
parseConfig(); parseConfig();
Entity.setCollectionDAO(jdbi.onDemand(CollectionDAO.class));
SystemRepository systemRepository = new SystemRepository();
Settings updatedSettings = Settings updatedSettings =
new Settings() new Settings()
.withConfigType(SettingsType.EMAIL_CONFIGURATION) .withConfigType(SettingsType.EMAIL_CONFIGURATION)
.withConfigValue(config.getSmtpSettings()); .withConfigValue(config.getSmtpSettings());
systemRepository.createOrUpdate(updatedSettings); Entity.getSystemRepository().createOrUpdate(updatedSettings);
LOG.info("Synced Email Configuration from Environment.");
return 0; return 0;
} catch (Exception e) { } catch (Exception e) {
LOG.error("Email Sync failed due to ", e); LOG.error("Email Sync failed due to ", e);
@ -221,6 +226,58 @@ public class OpenMetadataOperations implements Callable<Integer> {
} }
} }
@Command(name = "reset-password", description = "Reset the password for a user.")
public Integer resetUserPassword(
@Option(
names = {"-e", "--email"},
description = "Email for which to reset the password.",
required = true)
String email,
@Option(
names = {"-p", "--password"},
description = "Enter user password",
arity = "0..1",
interactive = true,
required = true)
char[] password) {
try {
LOG.info("Resetting password for user : {}", email);
if (nullOrEmpty(password)) {
throw new IllegalArgumentException("Password cannot be empty.");
}
parseConfig();
CollectionRegistry.initialize();
AuthProvider authProvider = config.getAuthenticationConfiguration().getProvider();
// Only Basic Auth provider is supported for password reset
if (!authProvider.equals(AuthProvider.BASIC)) {
LOG.error("Auth Provider is Not Basic. Cannot apply Password");
return 1;
}
UserRepository userRepository = (UserRepository) Entity.getEntityRepository(Entity.USER);
Set<String> fieldList = new HashSet<>(userRepository.getPatchFields().getFieldList());
fieldList.add(AUTH_MECHANISM_FIELD);
User originalUser = userRepository.getByEmail(null, email, new EntityUtil.Fields(fieldList));
// Check if the user is a bot user
if (Boolean.TRUE.equals(originalUser.getIsBot())) {
LOG.error("Bot user : {} cannot have password.", originalUser.getName());
return 1;
}
User updatedUser = JsonUtils.deepCopy(originalUser, User.class);
String inputPwd = new String(password);
updateUserWithHashedPwd(updatedUser, inputPwd);
UserUtil.addOrUpdateUser(updatedUser);
LOG.info("Password updated successfully.");
return 0;
} catch (Exception e) {
LOG.error("Failed to reset user password.", e);
return 1;
}
}
@Command( @Command(
name = "migrate", name = "migrate",
description = "Migrates the OpenMetadata database schema and search index mappings.") description = "Migrates the OpenMetadata database schema and search index mappings.")
@ -652,6 +709,7 @@ public class OpenMetadataOperations implements Callable<Integer> {
collectionDAO = jdbi.onDemand(CollectionDAO.class); collectionDAO = jdbi.onDemand(CollectionDAO.class);
Entity.setSearchRepository(searchRepository); Entity.setSearchRepository(searchRepository);
Entity.setCollectionDAO(collectionDAO); Entity.setCollectionDAO(collectionDAO);
Entity.setSystemRepository(new SystemRepository());
Entity.initializeRepositories(config, jdbi); Entity.initializeRepositories(config, jdbi);
} }

View File

@ -19,6 +19,7 @@ import static org.openmetadata.schema.entity.teams.AuthenticationMechanism.AuthT
import static org.openmetadata.schema.type.Include.NON_DELETED; import static org.openmetadata.schema.type.Include.NON_DELETED;
import static org.openmetadata.service.Entity.ADMIN_ROLE; import static org.openmetadata.service.Entity.ADMIN_ROLE;
import static org.openmetadata.service.Entity.ADMIN_USER_NAME; import static org.openmetadata.service.Entity.ADMIN_USER_NAME;
import static org.openmetadata.service.jdbi3.UserRepository.AUTH_MECHANISM_FIELD;
import at.favre.lib.crypto.bcrypt.BCrypt; import at.favre.lib.crypto.bcrypt.BCrypt;
import java.util.ArrayList; import java.util.ArrayList;
@ -62,8 +63,18 @@ public final class UserUtil {
public static void addUsers( public static void addUsers(
AuthProvider authProvider, Set<String> adminUsers, String domain, Boolean isAdmin) { AuthProvider authProvider, Set<String> adminUsers, String domain, Boolean isAdmin) {
try { try {
for (String username : adminUsers) { for (String keyValue : adminUsers) {
createOrUpdateUser(authProvider, username, domain, isAdmin); String userName = "";
String password = "";
if (keyValue.contains(":")) {
String[] keyValueArray = keyValue.split(":");
userName = keyValueArray[0];
password = keyValueArray[1];
} else {
userName = keyValue;
password = getPassword(userName);
}
createOrUpdateUser(authProvider, userName, password, domain, isAdmin);
} }
} catch (Exception ex) { } catch (Exception ex) {
LOG.error("[BootstrapUser] Encountered Exception while bootstrapping admin user", ex); LOG.error("[BootstrapUser] Encountered Exception while bootstrapping admin user", ex);
@ -71,27 +82,26 @@ public final class UserUtil {
} }
private static void createOrUpdateUser( private static void createOrUpdateUser(
AuthProvider authProvider, String username, String domain, Boolean isAdmin) { AuthProvider authProvider, String username, String password, String domain, Boolean isAdmin) {
UserRepository userRepository = (UserRepository) Entity.getEntityRepository(Entity.USER); UserRepository userRepository = (UserRepository) Entity.getEntityRepository(Entity.USER);
User updatedUser = null; User updatedUser = null;
try { try {
// Create Required Fields List // Create Required Fields List
Set<String> fieldList = new HashSet<>(userRepository.getPatchFields().getFieldList()); Set<String> fieldList = new HashSet<>(userRepository.getPatchFields().getFieldList());
fieldList.add("authenticationMechanism"); fieldList.add(AUTH_MECHANISM_FIELD);
// Fetch Original User, is available // Fetch Original User, is available
User originalUser = userRepository.getByName(null, username, new Fields(fieldList)); User originalUser = userRepository.getByName(null, username, new Fields(fieldList));
if (Boolean.FALSE.equals(originalUser.getIsBot()) if (Boolean.FALSE.equals(originalUser.getIsBot())
&& Boolean.FALSE.equals(originalUser.getIsAdmin())) { && Boolean.TRUE.equals(originalUser.getIsAdmin())) {
updatedUser = originalUser; updatedUser = originalUser;
// Update Auth Mechanism if not present, and send mail to the user // Update Auth Mechanism if not present, and send mail to the user
if (authProvider.equals(AuthProvider.BASIC)) { if (authProvider.equals(AuthProvider.BASIC)) {
if (originalUser.getAuthenticationMechanism() == null if (originalUser.getAuthenticationMechanism() == null
|| originalUser.getAuthenticationMechanism().equals(new AuthenticationMechanism())) { || originalUser.getAuthenticationMechanism().equals(new AuthenticationMechanism())) {
String randomPwd = getPassword(username); updateUserWithHashedPwd(updatedUser, password);
updateUserWithHashedPwd(updatedUser, randomPwd); EmailUtil.sendInviteMailToAdmin(updatedUser, password);
EmailUtil.sendInviteMailToAdmin(updatedUser, randomPwd);
} }
} else { } else {
updatedUser.setAuthenticationMechanism(new AuthenticationMechanism()); updatedUser.setAuthenticationMechanism(new AuthenticationMechanism());
@ -114,9 +124,8 @@ public final class UserUtil {
updatedUser = user(username, domain, username).withIsAdmin(isAdmin).withIsEmailVerified(true); updatedUser = user(username, domain, username).withIsAdmin(isAdmin).withIsEmailVerified(true);
// Update Auth Mechanism if not present, and send mail to the user // Update Auth Mechanism if not present, and send mail to the user
if (authProvider.equals(AuthProvider.BASIC)) { if (authProvider.equals(AuthProvider.BASIC)) {
String randomPwd = getPassword(username); updateUserWithHashedPwd(updatedUser, password);
updateUserWithHashedPwd(updatedUser, randomPwd); EmailUtil.sendInviteMailToAdmin(updatedUser, password);
EmailUtil.sendInviteMailToAdmin(updatedUser, randomPwd);
} }
} }