diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java b/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java index 13a6045ba55..83915ff063c 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java @@ -74,7 +74,6 @@ import org.openmetadata.service.jdbi3.locator.ConnectionAwareAnnotationSqlLocato import org.openmetadata.service.migration.Migration; import org.openmetadata.service.migration.MigrationConfiguration; import org.openmetadata.service.resources.CollectionRegistry; -import org.openmetadata.service.resources.tags.TagLabelCache; import org.openmetadata.service.secrets.SecretsManager; import org.openmetadata.service.secrets.SecretsManagerFactory; import org.openmetadata.service.secrets.SecretsManagerUpdateService; @@ -105,9 +104,7 @@ public class OpenMetadataApplication extends Application { static final String BOT_UPDATE_FIELDS = "botUser"; diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/UserRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/UserRepository.java index 2297b06fb4c..6fc150d915f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/UserRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/UserRepository.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; import javax.ws.rs.core.UriInfo; import lombok.extern.slf4j.Slf4j; import org.openmetadata.schema.api.teams.CreateTeam.TeamType; +import org.openmetadata.schema.auth.SSOAuthMechanism; import org.openmetadata.schema.entity.teams.AuthenticationMechanism; import org.openmetadata.schema.entity.teams.Team; import org.openmetadata.schema.entity.teams.User; @@ -34,15 +35,18 @@ import org.openmetadata.schema.type.EntityReference; import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Relationship; import org.openmetadata.service.Entity; +import org.openmetadata.service.OpenMetadataApplicationConfig; import org.openmetadata.service.exception.CatalogExceptionMessage; import org.openmetadata.service.jdbi3.CollectionDAO.EntityRelationshipRecord; import org.openmetadata.service.resources.teams.UserResource; import org.openmetadata.service.secrets.SecretsManager; import org.openmetadata.service.secrets.SecretsManagerFactory; +import org.openmetadata.service.security.SecurityUtil; import org.openmetadata.service.security.policyevaluator.SubjectCache; import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.EntityUtil.Fields; import org.openmetadata.service.util.JsonUtils; +import org.openmetadata.service.util.UserUtil; @Slf4j public class UserRepository extends EntityRepository { @@ -181,6 +185,22 @@ public class UserRepository extends EntityRepository { return daoCollection.userDAO().checkEmailExists(emailId) > 0; } + public void initializeUsers(OpenMetadataApplicationConfig config) { + Set adminUsers = new HashSet<>(config.getAuthorizerConfiguration().getAdminPrincipals()); + LOG.debug("Checking user entries for admin users {}", adminUsers); + String domain = SecurityUtil.getDomain(config); + String providerType = config.getAuthenticationConfiguration().getProvider(); + if (providerType.equals(SSOAuthMechanism.SsoServiceType.BASIC.value())) { + UserUtil.handleBasicAuth(adminUsers, domain); + } else { + UserUtil.addUsers(adminUsers, domain, true); + } + + LOG.debug("Checking user entries for test users"); + Set testUsers = new HashSet<>(config.getAuthorizerConfiguration().getTestPrincipals()); + UserUtil.addUsers(testUsers, domain, null); + } + private List getOwns(User user) throws IOException { // Compile entities owned by the user List ownedEntities = diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java index f804cde480d..3d8c77bf39d 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java @@ -13,8 +13,6 @@ package org.openmetadata.service.resources.bots; -import static org.openmetadata.service.security.DefaultAuthorizer.user; - import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.ExternalDocumentation; import io.swagger.v3.oas.annotations.Operation; @@ -67,18 +65,18 @@ import org.openmetadata.service.resources.Collection; import org.openmetadata.service.resources.EntityResource; import org.openmetadata.service.resources.teams.RoleResource; import org.openmetadata.service.security.Authorizer; -import org.openmetadata.service.security.DefaultAuthorizer; import org.openmetadata.service.security.SecurityUtil; import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.EntityUtil.Fields; import org.openmetadata.service.util.ResultList; +import org.openmetadata.service.util.UserUtil; @Slf4j @Path("/v1/bots") @Api(value = "Bot collection", tags = "Bot collection") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -@Collection(name = "bots", order = 8) // initialize after user resource +@Collection(name = "bots", order = 4) // initialize after user resource public class BotResource extends EntityResource { public static final String COLLECTION_PATH = "/v1/bots/"; @@ -93,12 +91,12 @@ public class BotResource extends EntityResource { String domain = SecurityUtil.getDomain(config); for (Bot bot : bots) { String userName = bot.getBotUser().getName(); - User user = user(userName, domain, userName).withIsBot(true).withIsAdmin(false); + User user = UserUtil.user(userName, domain, userName).withIsBot(true).withIsAdmin(false); // Add role corresponding to the bot to the user // we need to set a mutable list here user.setRoles(Arrays.asList(RoleResource.getRole(getRoleForBot(bot.getName())))); - user = DefaultAuthorizer.addOrUpdateBotUser(user, config); + user = UserUtil.addOrUpdateBotUser(user, config); bot.withId(UUID.randomUUID()) .withBotUser(user.getEntityReference()) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java index 33509b84dd7..58806d870c7 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java @@ -64,7 +64,7 @@ import org.openmetadata.service.util.ResultList; @Api(value = "Glossary collection", tags = "Glossary collection") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -@Collection(name = "glossaries") +@Collection(name = "glossaries", order = 4) public class GlossaryResource extends EntityResource { public static final String COLLECTION_PATH = "v1/glossaries/"; diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java index b7573ef4537..08736fb28c4 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java @@ -68,7 +68,7 @@ import org.openmetadata.service.util.ResultList; @Api(value = "Glossary collection", tags = "Glossary collection") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -@Collection(name = "glossaries") +@Collection(name = "glossaries", order = 5) public class GlossaryTermResource extends EntityResource { public static final String COLLECTION_PATH = "v1/glossaryTerms/"; diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java index f58cce30bdb..c49eaf7b2da 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java @@ -102,6 +102,7 @@ public class PolicyResource extends EntityResource { // Load any existing rules from database, before loading seed data. dao.initSeedDataFromResources(); ResourceRegistry.initialize(listOrEmpty(PolicyResource.getResourceDescriptors())); + PolicyCache.initialize(); } @Override diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java index 465dc90bc2a..54aee76e4b6 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java @@ -75,7 +75,7 @@ import org.openmetadata.service.util.ResultList; @Api(value = "Tags resources collection", tags = "Tags resources collection") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -@Collection(name = "tags") +@Collection(name = "tags", order = 6) // initialize after Glossary and GlossaryTerm public class TagResource { public static final String TAG_COLLECTION_PATH = "/v1/tags/"; private final TagRepository dao; @@ -119,6 +119,7 @@ public class TagResource { .withProvider(tagCategory.getProvider())); }); daoCategory.initCategory(tagCategory); + TagLabelCache.initialize(); } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java index 49f4731de46..0eb7b07dccf 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java @@ -102,6 +102,7 @@ public class RoleResource extends EntityResource { } dao.initializeEntity(role); } + RoleCache.initialize(); } public static class RoleList extends ResultList { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java index 4f1a7bb930a..9a0da7b98e1 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java @@ -71,7 +71,7 @@ import org.openmetadata.service.util.ResultList; @Api(value = "Teams collection", tags = "Teams collection") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -@Collection(name = "teams", order = 3) // Load after roles, and policy resources +@Collection(name = "teams", order = 2) // Load after roles, and policy resources public class TeamResource extends EntityResource { public static final String COLLECTION_PATH = "/v1/teams/"; diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java index 140d45c9238..5d2f49f9c9b 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java @@ -110,6 +110,7 @@ import org.openmetadata.service.security.auth.BotTokenCache; import org.openmetadata.service.security.jwt.JWTTokenGenerator; import org.openmetadata.service.security.policyevaluator.OperationContext; import org.openmetadata.service.security.policyevaluator.ResourceContext; +import org.openmetadata.service.security.policyevaluator.SubjectCache; import org.openmetadata.service.security.saml.JwtTokenCacheManager; import org.openmetadata.service.util.EmailUtil; import org.openmetadata.service.util.EntityUtil; @@ -124,7 +125,7 @@ import org.openmetadata.service.util.ResultList; @Api(value = "User collection", tags = "User collection") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -@Collection(name = "users", order = 7) // Initialize user resource before bot resource (at default order 9) +@Collection(name = "users", order = 3) // Initialize user resource before bot resource (at default order 9) public class UserResource extends EntityResource { public static final String COLLECTION_PATH = "v1/users/"; public static final String USER_PROTECTED_FIELDS = "authenticationMechanism"; @@ -157,6 +158,8 @@ public class UserResource extends EntityResource { this.authenticationConfiguration = config.getAuthenticationConfiguration(); SmtpSettings smtpSettings = config.getSmtpSettings(); this.isEmailServiceEnabled = smtpSettings != null && smtpSettings.getEnableSmtpServer(); + this.dao.initializeUsers(config); + SubjectCache.initialize(); } public static class UserList extends ResultList { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/DefaultAuthorizer.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/DefaultAuthorizer.java index 5821015752c..a1fe10c8613 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/security/DefaultAuthorizer.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/DefaultAuthorizer.java @@ -13,171 +13,28 @@ package org.openmetadata.service.security; -import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty; -import static org.openmetadata.schema.auth.SSOAuthMechanism.SsoServiceType.AUTH_0; -import static org.openmetadata.schema.auth.SSOAuthMechanism.SsoServiceType.AZURE; -import static org.openmetadata.schema.auth.SSOAuthMechanism.SsoServiceType.CUSTOM_OIDC; -import static org.openmetadata.schema.auth.SSOAuthMechanism.SsoServiceType.GOOGLE; -import static org.openmetadata.schema.auth.SSOAuthMechanism.SsoServiceType.OKTA; -import static org.openmetadata.schema.entity.teams.AuthenticationMechanism.AuthType.JWT; -import static org.openmetadata.schema.entity.teams.AuthenticationMechanism.AuthType.SSO; import static org.openmetadata.schema.type.Permission.Access.ALLOW; -import static org.openmetadata.service.Entity.ADMIN_USER_NAME; import static org.openmetadata.service.exception.CatalogExceptionMessage.notAdmin; -import static org.openmetadata.service.resources.teams.UserResource.USER_PROTECTED_FIELDS; -import at.favre.lib.crypto.bcrypt.BCrypt; import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; import javax.ws.rs.core.SecurityContext; import lombok.extern.slf4j.Slf4j; import org.jdbi.v3.core.Jdbi; -import org.openmetadata.schema.api.configuration.airflow.AirflowConfiguration; -import org.openmetadata.schema.api.security.AuthenticationConfiguration; -import org.openmetadata.schema.auth.BasicAuthMechanism; -import org.openmetadata.schema.auth.JWTAuthMechanism; -import org.openmetadata.schema.auth.JWTTokenExpiry; -import org.openmetadata.schema.auth.SSOAuthMechanism; -import org.openmetadata.schema.email.SmtpSettings; -import org.openmetadata.schema.entity.teams.AuthenticationMechanism; -import org.openmetadata.schema.entity.teams.User; -import org.openmetadata.schema.security.client.OpenMetadataJWTClientConfig; import org.openmetadata.schema.type.ResourcePermission; -import org.openmetadata.service.Entity; import org.openmetadata.service.OpenMetadataApplicationConfig; -import org.openmetadata.service.exception.EntityNotFoundException; -import org.openmetadata.service.jdbi3.EntityRepository; -import org.openmetadata.service.jdbi3.UserRepository; -import org.openmetadata.service.security.jwt.JWTTokenGenerator; import org.openmetadata.service.security.policyevaluator.OperationContext; -import org.openmetadata.service.security.policyevaluator.PolicyCache; import org.openmetadata.service.security.policyevaluator.PolicyEvaluator; import org.openmetadata.service.security.policyevaluator.ResourceContextInterface; -import org.openmetadata.service.security.policyevaluator.RoleCache; import org.openmetadata.service.security.policyevaluator.SubjectCache; import org.openmetadata.service.security.policyevaluator.SubjectContext; -import org.openmetadata.service.util.EmailUtil; -import org.openmetadata.service.util.EntityUtil; -import org.openmetadata.service.util.PasswordUtil; -import org.openmetadata.service.util.RestUtil; @Slf4j public class DefaultAuthorizer implements Authorizer { - private static final String COLON_DELIMITER = ":"; - private boolean isSmtpEnabled; @Override public void init(OpenMetadataApplicationConfig config, Jdbi dbi) { LOG.info("Initializing DefaultAuthorizer with config {}", config.getAuthorizerConfiguration()); - SmtpSettings smtpSettings = config.getSmtpSettings(); - this.isSmtpEnabled = smtpSettings != null && smtpSettings.getEnableSmtpServer(); - SubjectCache.initialize(); - PolicyCache.initialize(); - RoleCache.initialize(); - initializeUsers(config); - } - - private void initializeUsers(OpenMetadataApplicationConfig config) { - Set adminUsers = new HashSet<>(config.getAuthorizerConfiguration().getAdminPrincipals()); - LOG.debug("Checking user entries for admin users {}", adminUsers); - String domain = SecurityUtil.getDomain(config); - String providerType = config.getAuthenticationConfiguration().getProvider(); - if (providerType.equals(SSOAuthMechanism.SsoServiceType.BASIC.value())) { - handleBasicAuth(adminUsers, domain); - } else { - addUsers(adminUsers, domain, true); - } - - LOG.debug("Checking user entries for test users"); - Set testUsers = new HashSet<>(config.getAuthorizerConfiguration().getTestPrincipals()); - addUsers(testUsers, domain, null); - } - - private void handleBasicAuth(Set adminUsers, String domain) { - try { - for (String adminUser : adminUsers) { - if (adminUser.contains(COLON_DELIMITER)) { - String[] tokens = adminUser.split(COLON_DELIMITER); - addUserForBasicAuth(tokens[0], tokens[1], domain); - } else { - boolean isDefaultAdmin = adminUser.equals(ADMIN_USER_NAME); - String token = PasswordUtil.generateRandomPassword(); - if (isDefaultAdmin || !isSmtpEnabled) { - token = ADMIN_USER_NAME; - } - addUserForBasicAuth(adminUser, token, domain); - } - } - } catch (IOException e) { - LOG.error("Failed in Basic Auth Setup. Reason : {}", e.getMessage()); - } - } - - private void addUserForBasicAuth(String username, String pwd, String domain) throws IOException { - EntityRepository userRepository = Entity.getEntityRepository(Entity.USER); - User originalUser; - try { - List fields = userRepository.getAllowedFieldsCopy(); - fields.add(USER_PROTECTED_FIELDS); - originalUser = userRepository.getByName(null, username, new EntityUtil.Fields(fields, String.join(",", fields))); - if (originalUser.getAuthenticationMechanism() == null) { - updateUserWithHashedPwd(originalUser, pwd); - } - addOrUpdateUser(originalUser); - } catch (EntityNotFoundException e) { - // TODO: Not the best way ! :( - User user = user(username, domain, username).withIsAdmin(true).withIsEmailVerified(true); - updateUserWithHashedPwd(user, pwd); - addOrUpdateUser(user); - if (isSmtpEnabled) { - sendInviteMailToAdmin(user, pwd); - } - } - } - - private void sendInviteMailToAdmin(User user, String pwd) { - Map templatePopulator = new HashMap<>(); - templatePopulator.put(EmailUtil.ENTITY, EmailUtil.getInstance().getEmailingEntity()); - templatePopulator.put(EmailUtil.SUPPORT_URL, EmailUtil.getInstance().getSupportUrl()); - templatePopulator.put(EmailUtil.USERNAME, user.getName()); - templatePopulator.put(EmailUtil.PASSWORD, pwd); - templatePopulator.put(EmailUtil.APPLICATION_LOGIN_LINK, EmailUtil.getInstance().getOMUrl()); - try { - EmailUtil.getInstance() - .sendMail( - EmailUtil.getInstance().getEmailInviteSubject(), - templatePopulator, - user.getEmail(), - EmailUtil.EMAIL_TEMPLATE_BASEPATH, - EmailUtil.INVITE_RANDOM_PWD); - } catch (Exception ex) { - LOG.error("Failed in sending Mail to user [{}]. Reason : {}", user.getEmail(), ex.getMessage()); - } - } - - private void updateUserWithHashedPwd(User user, String pwd) { - String hashedPwd = BCrypt.withDefaults().hashToString(12, pwd.toCharArray()); - user.setAuthenticationMechanism( - new AuthenticationMechanism() - .withAuthType(AuthenticationMechanism.AuthType.BASIC) - .withConfig(new BasicAuthMechanism().withPassword(hashedPwd))); - } - - public static User user(String name, String domain, String updatedBy) { - return new User() - .withId(UUID.randomUUID()) - .withName(name) - .withFullyQualifiedName(name) - .withEmail(name + "@" + domain) - .withUpdatedBy(updatedBy) - .withUpdatedAt(System.currentTimeMillis()) - .withIsBot(false); } @Override @@ -234,130 +91,6 @@ public class DefaultAuthorizer implements Authorizer { return subjectContext.isAdmin() || subjectContext.isBot(); } - private void addUsers(Set users, String domain, Boolean isAdmin) { - for (String userName : users) { - User user = user(userName, domain, userName).withIsAdmin(isAdmin); - addOrUpdateUser(user); - } - } - - private static User addOrUpdateUser(User user) { - EntityRepository userRepository = Entity.getEntityRepository(Entity.USER); - try { - RestUtil.PutResponse addedUser = userRepository.createOrUpdate(null, user); - // should not log the user auth details in LOGS - LOG.debug("Added user entry: {}", addedUser.getEntity().getName()); - return addedUser.getEntity(); - } catch (Exception exception) { - // In HA set up the other server may have already added the user. - LOG.debug("Caught exception", exception); - user.setAuthenticationMechanism(null); - LOG.debug("User entry: {} already exists.", user.getName()); - } - return null; - } - - /** - * This method add auth mechanism in the following way: - * - *
    - *
  • If original user has already an authMechanism, add it to the user - *
  • Otherwise: - *
      - *
    • If airflow configuration is 'openmetadata' and server auth provider is not basic, add JWT auth - * mechanism from Airflow configuration - *
    • Otherwise: - *
        - *
      • If airflow configuration is 'basic', add JWT auth mechanism with a generated token which does not - * expire - *
      • Otherwise, add SSO auth mechanism from Airflow configuration - *
      - *
    - *
- */ - public static User addOrUpdateBotUser(User user, OpenMetadataApplicationConfig openMetadataApplicationConfig) { - User originalUser = retrieveWithAuthMechanism(user); - // the user did not have an auth mechanism - AuthenticationMechanism authMechanism = originalUser != null ? originalUser.getAuthenticationMechanism() : null; - if (authMechanism == null) { - AuthenticationConfiguration authConfig = openMetadataApplicationConfig.getAuthenticationConfiguration(); - AirflowConfiguration airflowConfig = openMetadataApplicationConfig.getAirflowConfiguration(); - // if the auth provider is "openmetadata" in the configuration set JWT as auth mechanism - if ("openmetadata".equals(airflowConfig.getAuthProvider()) && !"basic".equals(authConfig.getProvider())) { - OpenMetadataJWTClientConfig jwtClientConfig = airflowConfig.getAuthConfig().getOpenmetadata(); - authMechanism = buildAuthMechanism(JWT, buildJWTAuthMechanism(jwtClientConfig, user)); - } else { - // Otherwise, set auth mechanism from airflow configuration - // TODO: https://github.com/open-metadata/OpenMetadata/issues/7712 - if (airflowConfig.getAuthConfig() != null && !"basic".equals(authConfig.getProvider())) { - switch (authConfig.getProvider()) { - case "no-auth": - break; - case "azure": - authMechanism = - buildAuthMechanism(SSO, buildAuthMechanismConfig(AZURE, airflowConfig.getAuthConfig().getAzure())); - break; - case "google": - authMechanism = - buildAuthMechanism(SSO, buildAuthMechanismConfig(GOOGLE, airflowConfig.getAuthConfig().getGoogle())); - break; - case "okta": - authMechanism = - buildAuthMechanism(SSO, buildAuthMechanismConfig(OKTA, airflowConfig.getAuthConfig().getOkta())); - break; - case "auth0": - authMechanism = - buildAuthMechanism(SSO, buildAuthMechanismConfig(AUTH_0, airflowConfig.getAuthConfig().getAuth0())); - break; - case "custom-oidc": - authMechanism = - buildAuthMechanism( - SSO, buildAuthMechanismConfig(CUSTOM_OIDC, airflowConfig.getAuthConfig().getCustomOidc())); - break; - default: - throw new IllegalArgumentException( - String.format( - "Unexpected auth provider [%s] for bot [%s]", authConfig.getProvider(), user.getName())); - } - } else if ("basic".equals(authConfig.getProvider())) { - authMechanism = buildAuthMechanism(JWT, buildJWTAuthMechanism(null, user)); - } - } - } - user.setAuthenticationMechanism(authMechanism); - user.setDescription(user.getDescription()); - user.setDisplayName(user.getDisplayName()); - user.setUpdatedBy(ADMIN_USER_NAME); - return addOrUpdateUser(user); - } - - private static JWTAuthMechanism buildJWTAuthMechanism(OpenMetadataJWTClientConfig jwtClientConfig, User user) { - return Objects.isNull(jwtClientConfig) || nullOrEmpty(jwtClientConfig.getJwtToken()) - ? JWTTokenGenerator.getInstance().generateJWTToken(user, JWTTokenExpiry.Unlimited) - : new JWTAuthMechanism() - .withJWTToken(jwtClientConfig.getJwtToken()) - .withJWTTokenExpiry(JWTTokenExpiry.Unlimited); - } - - private static SSOAuthMechanism buildAuthMechanismConfig( - SSOAuthMechanism.SsoServiceType ssoServiceType, Object config) { - return new SSOAuthMechanism().withSsoServiceType(ssoServiceType).withAuthConfig(config); - } - - private static AuthenticationMechanism buildAuthMechanism(AuthenticationMechanism.AuthType authType, Object config) { - return new AuthenticationMechanism().withAuthType(authType).withConfig(config); - } - - private static User retrieveWithAuthMechanism(User user) { - EntityRepository userRepository = UserRepository.class.cast(Entity.getEntityRepository(Entity.USER)); - try { - return userRepository.getByName(null, user.getName(), new EntityUtil.Fields(List.of("authenticationMechanism"))); - } catch (IOException | EntityNotFoundException e) { - LOG.debug("Bot entity: {} does not exists.", user); - return null; - } - } - public static SubjectContext getSubjectContext(SecurityContext securityContext) { if (securityContext == null || securityContext.getUserPrincipal() == null) { throw new AuthenticationException("No principal in security context"); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/util/EmailUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/util/EmailUtil.java index 4d9e7e1be25..178fd25c34c 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/util/EmailUtil.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/util/EmailUtil.java @@ -15,11 +15,11 @@ import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.openmetadata.common.utils.CommonUtil; -import org.openmetadata.schema.email.EmailRequest; import org.openmetadata.schema.email.SmtpSettings; import org.openmetadata.schema.entity.feed.Thread; import org.openmetadata.schema.entity.teams.User; import org.openmetadata.schema.tests.type.TestCaseResult; +import org.openmetadata.service.OpenMetadataApplicationConfig; import org.simplejavamail.api.email.Email; import org.simplejavamail.api.email.EmailPopulatingBuilder; import org.simplejavamail.api.mailer.Mailer; @@ -58,22 +58,35 @@ public class EmailUtil { public static final String INVITE_CREATE_PWD = "invite-createPassword.ftl"; public static final String TASK_NOTIFICATION_TEMPLATE = "taskAssignment.ftl"; public static final String TEST_NOTIFICATION_TEMPLATE = "testResultStatus.ftl"; - private static EmailUtil INSTANCE = null; - private SmtpSettings defaultSmtpSettings = null; - private Mailer mailer = null; - private Configuration templateConfiguration = null; + private static EmailUtil INSTANCE; + private static SmtpSettings DEFAULT_SMTP_SETTINGS; + private static Mailer MAILER; + private static Configuration TEMPLATE_CONFIGURATION; + private static volatile boolean INITIALIZED = false; - private EmailUtil(SmtpSettings smtpServerSettings) { - try { - this.defaultSmtpSettings = smtpServerSettings; - this.mailer = this.createMailer(smtpServerSettings); - this.templateConfiguration = new Configuration(VERSION_2_3_28); - } catch (Exception ex) { - LOG.warn("[MAILER] Smtp Configurations are missing : Reason {} ", ex.getMessage(), ex); + public static void initialize(OpenMetadataApplicationConfig config) { + if (!INITIALIZED) { + if (config.getSmtpSettings() != null && config.getSmtpSettings().getEnableSmtpServer()) { + try { + DEFAULT_SMTP_SETTINGS = config.getSmtpSettings(); + MAILER = createMailer(DEFAULT_SMTP_SETTINGS); + TEMPLATE_CONFIGURATION = new Configuration(VERSION_2_3_28); + LOG.info("Email Util cache is initialized"); + } catch (Exception ex) { + LOG.warn("[MAILER] Smtp Configurations are missing : Reason {} ", ex.getMessage(), ex); + } + } else { + DEFAULT_SMTP_SETTINGS = new SmtpSettings(); + } + INSTANCE = new EmailUtil(); + INITIALIZED = true; + } else { + INITIALIZED = false; + LOG.info("Email Util is already initialized"); } } - private Mailer createMailer(SmtpSettings smtpServerSettings) { + private static Mailer createMailer(SmtpSettings smtpServerSettings) { TransportStrategy strategy; switch (smtpServerSettings.getTransportationStrategy()) { case SMPTS: @@ -100,10 +113,10 @@ public class EmailUtil { } public void sendAccountStatus(User user, String action, String status) throws IOException, TemplateException { - if (defaultSmtpSettings.getEnableSmtpServer()) { + if (DEFAULT_SMTP_SETTINGS.getEnableSmtpServer()) { Map templatePopulator = new HashMap<>(); - templatePopulator.put(ENTITY, EmailUtil.getInstance().getEmailingEntity()); - templatePopulator.put(SUPPORT_URL, EmailUtil.getInstance().getSupportUrl()); + templatePopulator.put(ENTITY, getEmailingEntity()); + templatePopulator.put(SUPPORT_URL, getSupportUrl()); templatePopulator.put(USERNAME, user.getName()); templatePopulator.put(ACTION_KEY, action); templatePopulator.put(ACTION_STATUS_KEY, status); @@ -117,7 +130,7 @@ public class EmailUtil { } public void sendEmailVerification(String emailVerificationLink, User user) throws IOException, TemplateException { - if (defaultSmtpSettings.getEnableSmtpServer()) { + if (DEFAULT_SMTP_SETTINGS.getEnableSmtpServer()) { Map templatePopulator = new HashMap<>(); templatePopulator.put(ENTITY, getEmailingEntity()); templatePopulator.put(SUPPORT_URL, getSupportUrl()); @@ -135,7 +148,7 @@ public class EmailUtil { public void sendPasswordResetLink(String passwordResetLink, User user, String subject, String templateFilePath) throws IOException, TemplateException { - if (defaultSmtpSettings.getEnableSmtpServer()) { + if (DEFAULT_SMTP_SETTINGS.getEnableSmtpServer()) { Map templatePopulator = new HashMap<>(); templatePopulator.put(ENTITY, getEmailingEntity()); templatePopulator.put(SUPPORT_URL, getSupportUrl()); @@ -150,7 +163,7 @@ public class EmailUtil { public void sendTaskAssignmentNotificationToUser( String assigneeName, String email, String taskLink, Thread thread, String subject, String templateFilePath) throws IOException, TemplateException { - if (defaultSmtpSettings.getEnableSmtpServer()) { + if (DEFAULT_SMTP_SETTINGS.getEnableSmtpServer()) { Map templatePopulator = new HashMap<>(); templatePopulator.put("assignee", assigneeName); templatePopulator.put("createdBy", thread.getCreatedBy()); @@ -173,7 +186,7 @@ public class EmailUtil { String subject, String templateFilePath) throws IOException, TemplateException { - if (defaultSmtpSettings.getEnableSmtpServer()) { + if (DEFAULT_SMTP_SETTINGS.getEnableSmtpServer()) { Map templatePopulator = new HashMap<>(); templatePopulator.put("receiverName", email.split("@")[0]); templatePopulator.put("testResultName", testCaseName); @@ -186,84 +199,17 @@ public class EmailUtil { } } - public Email buildEmailWithDefaultSender(EmailRequest request) { - EmailPopulatingBuilder emailBuilder = EmailBuilder.startingBlank(); - if (request.getRecipientMails() != null - && request.getRecipientMails().size() != 0 - && request.getSubject() != null - && !request.getSubject().equals("")) { - // Sender Details - emailBuilder.from(defaultSmtpSettings.getSenderMail()); - - // Recipient - request - .getRecipientMails() - .forEach( - (pair) -> { - if (pair.getName() != null && !pair.getName().equals("")) { - emailBuilder.to(pair.getName(), pair.getEmail()); - } else { - emailBuilder.to(pair.getEmail()); - } - }); - - // CC - if (request.getCcMails() != null) { - request - .getCcMails() - .forEach( - (pair) -> { - if (pair.getName() != null && !pair.getName().equals("")) { - emailBuilder.cc(pair.getName(), pair.getEmail()); - } else { - emailBuilder.cc(pair.getEmail()); - } - }); - } - - // BCC - if (request.getBccMails() != null) { - request - .getBccMails() - .forEach( - (pair) -> { - if (pair.getName() != null && !pair.getName().equals("")) { - emailBuilder.bcc(pair.getName(), pair.getEmail()); - } else { - emailBuilder.bcc(pair.getEmail()); - } - }); - } - - // Subject - if (request.getSubject() != null) { - emailBuilder.withSubject(request.getSubject()); - } - - if (request.getContent() != null) { - if (request.getContentType() == EmailRequest.ContentType.HTML) { - emailBuilder.withHTMLText(request.getContent()); - } else { - emailBuilder.withPlainText(request.getContent()); - } - } - } else { - throw new RuntimeException("Email Request is missing Required Details"); - } - return emailBuilder.buildEmail(); - } - public void sendMail( String subject, Map model, String to, String baseTemplatePackage, String templatePath) throws IOException, TemplateException { - if (defaultSmtpSettings.getEnableSmtpServer()) { + if (DEFAULT_SMTP_SETTINGS.getEnableSmtpServer()) { EmailPopulatingBuilder emailBuilder = EmailBuilder.startingBlank(); emailBuilder.withSubject(subject); emailBuilder.to(to); - emailBuilder.from(defaultSmtpSettings.getSenderMail()); + emailBuilder.from(DEFAULT_SMTP_SETTINGS.getSenderMail()); - templateConfiguration.setClassForTemplateLoading(getClass(), baseTemplatePackage); - Template template = templateConfiguration.getTemplate(templatePath); + TEMPLATE_CONFIGURATION.setClassForTemplateLoading(getClass(), baseTemplatePackage); + Template template = TEMPLATE_CONFIGURATION.getTemplate(templatePath); // write the freemarker output to a StringWriter StringWriter stringWriter = new StringWriter(); @@ -275,17 +221,17 @@ public class EmailUtil { } public void sendMail(Email email) { - if (mailer != null && defaultSmtpSettings.getEnableSmtpServer()) { - mailer.sendMail(email, true); + if (MAILER != null && DEFAULT_SMTP_SETTINGS.getEnableSmtpServer()) { + MAILER.sendMail(email, true); } } public String buildBaseUrl(URI uri) { try { - if (CommonUtil.nullOrEmpty(this.defaultSmtpSettings.getOpenMetadataUrl())) { + if (CommonUtil.nullOrEmpty(DEFAULT_SMTP_SETTINGS.getOpenMetadataUrl())) { return String.format("%s://%s", uri.getScheme(), uri.getHost()); } else { - URI serverUrl = new URI(this.defaultSmtpSettings.getOpenMetadataUrl()); + URI serverUrl = new URI(DEFAULT_SMTP_SETTINGS.getOpenMetadataUrl()); return String.format("%s://%s", serverUrl.getScheme(), serverUrl.getHost()); } } catch (Exception ex) { @@ -293,53 +239,65 @@ public class EmailUtil { } } - public void testConnection() { - mailer.testConnection(); - } - - public static class EmailUtilBuilder { - private EmailUtilBuilder() {} - - public static void build(SmtpSettings smtpServerSettings) { - if (INSTANCE == null) { - INSTANCE = new EmailUtil(smtpServerSettings); + public static void sendInviteMailToAdmin(User user, String pwd) { + if (DEFAULT_SMTP_SETTINGS.getEnableSmtpServer()) { + Map templatePopulator = new HashMap<>(); + templatePopulator.put(EmailUtil.ENTITY, EmailUtil.getInstance().getEmailingEntity()); + templatePopulator.put(EmailUtil.SUPPORT_URL, EmailUtil.getInstance().getSupportUrl()); + templatePopulator.put(EmailUtil.USERNAME, user.getName()); + templatePopulator.put(EmailUtil.PASSWORD, pwd); + templatePopulator.put(EmailUtil.APPLICATION_LOGIN_LINK, EmailUtil.getInstance().getOMUrl()); + try { + EmailUtil.getInstance() + .sendMail( + EmailUtil.getInstance().getEmailInviteSubject(), + templatePopulator, + user.getEmail(), + EmailUtil.EMAIL_TEMPLATE_BASEPATH, + EmailUtil.INVITE_RANDOM_PWD); + } catch (Exception ex) { + LOG.error("Failed in sending Mail to user [{}]. Reason : {}", user.getEmail(), ex.getMessage()); } } } - public String getEmailVerificationSubject() { - return String.format(EMAIL_VERIFICATION_SUBJECT, defaultSmtpSettings.getEmailingEntity()); + public void testConnection() { + MAILER.testConnection(); + } + + private String getEmailVerificationSubject() { + return String.format(EMAIL_VERIFICATION_SUBJECT, DEFAULT_SMTP_SETTINGS.getEmailingEntity()); } public String getPasswordResetSubject() { - return String.format(PASSWORD_RESET_SUBJECT, defaultSmtpSettings.getEmailingEntity()); + return String.format(PASSWORD_RESET_SUBJECT, DEFAULT_SMTP_SETTINGS.getEmailingEntity()); } - public String getAccountStatusChangeSubject() { - return String.format(ACCOUNT_STATUS_SUBJECT, defaultSmtpSettings.getEmailingEntity()); + private String getAccountStatusChangeSubject() { + return String.format(ACCOUNT_STATUS_SUBJECT, DEFAULT_SMTP_SETTINGS.getEmailingEntity()); } public String getEmailInviteSubject() { - return String.format(INVITE_SUBJECT, defaultSmtpSettings.getEmailingEntity()); + return String.format(INVITE_SUBJECT, DEFAULT_SMTP_SETTINGS.getEmailingEntity()); } public String getTaskAssignmentSubject() { - return String.format(TASK_SUBJECT, defaultSmtpSettings.getEmailingEntity()); + return String.format(TASK_SUBJECT, DEFAULT_SMTP_SETTINGS.getEmailingEntity()); } public String getTestResultSubject() { - return String.format(TEST_SUBJECT, defaultSmtpSettings.getEmailingEntity()); + return String.format(TEST_SUBJECT, DEFAULT_SMTP_SETTINGS.getEmailingEntity()); } public String getEmailingEntity() { - return defaultSmtpSettings.getEmailingEntity(); + return DEFAULT_SMTP_SETTINGS.getEmailingEntity(); } public String getSupportUrl() { - return defaultSmtpSettings.getSupportUrl(); + return DEFAULT_SMTP_SETTINGS.getSupportUrl(); } public String getOMUrl() { - return defaultSmtpSettings.getOpenMetadataUrl(); + return DEFAULT_SMTP_SETTINGS.getOpenMetadataUrl(); } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/util/UserUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/util/UserUtil.java new file mode 100644 index 00000000000..94b0e8ab820 --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/util/UserUtil.java @@ -0,0 +1,223 @@ +package org.openmetadata.service.util; + +import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty; +import static org.openmetadata.schema.auth.SSOAuthMechanism.SsoServiceType.AUTH_0; +import static org.openmetadata.schema.auth.SSOAuthMechanism.SsoServiceType.AZURE; +import static org.openmetadata.schema.auth.SSOAuthMechanism.SsoServiceType.CUSTOM_OIDC; +import static org.openmetadata.schema.auth.SSOAuthMechanism.SsoServiceType.GOOGLE; +import static org.openmetadata.schema.auth.SSOAuthMechanism.SsoServiceType.OKTA; +import static org.openmetadata.schema.entity.teams.AuthenticationMechanism.AuthType.JWT; +import static org.openmetadata.schema.entity.teams.AuthenticationMechanism.AuthType.SSO; +import static org.openmetadata.service.Entity.ADMIN_USER_NAME; +import static org.openmetadata.service.resources.teams.UserResource.USER_PROTECTED_FIELDS; + +import at.favre.lib.crypto.bcrypt.BCrypt; +import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import lombok.extern.slf4j.Slf4j; +import org.openmetadata.schema.api.configuration.airflow.AirflowConfiguration; +import org.openmetadata.schema.api.security.AuthenticationConfiguration; +import org.openmetadata.schema.auth.BasicAuthMechanism; +import org.openmetadata.schema.auth.JWTAuthMechanism; +import org.openmetadata.schema.auth.JWTTokenExpiry; +import org.openmetadata.schema.auth.SSOAuthMechanism; +import org.openmetadata.schema.entity.teams.AuthenticationMechanism; +import org.openmetadata.schema.entity.teams.User; +import org.openmetadata.schema.security.client.OpenMetadataJWTClientConfig; +import org.openmetadata.service.Entity; +import org.openmetadata.service.OpenMetadataApplicationConfig; +import org.openmetadata.service.exception.EntityNotFoundException; +import org.openmetadata.service.jdbi3.EntityRepository; +import org.openmetadata.service.jdbi3.UserRepository; +import org.openmetadata.service.security.jwt.JWTTokenGenerator; + +@Slf4j +public final class UserUtil { + private static final String COLON_DELIMITER = ":"; + + public static void handleBasicAuth(Set adminUsers, String domain) { + try { + for (String adminUser : adminUsers) { + if (adminUser.contains(COLON_DELIMITER)) { + String[] tokens = adminUser.split(COLON_DELIMITER); + addUserForBasicAuth(tokens[0], tokens[1], domain); + } else { + boolean isDefaultAdmin = adminUser.equals(ADMIN_USER_NAME); + String token = PasswordUtil.generateRandomPassword(); + if (isDefaultAdmin) { + token = ADMIN_USER_NAME; + } + addUserForBasicAuth(adminUser, token, domain); + } + } + } catch (IOException e) { + LOG.error("Failed in Basic Auth Setup. Reason : {}", e.getMessage()); + } + } + + public static void addUserForBasicAuth(String username, String pwd, String domain) throws IOException { + EntityRepository userRepository = Entity.getEntityRepository(Entity.USER); + User originalUser; + try { + List fields = userRepository.getAllowedFieldsCopy(); + fields.add(USER_PROTECTED_FIELDS); + originalUser = userRepository.getByName(null, username, new EntityUtil.Fields(fields, String.join(",", fields))); + if (originalUser.getAuthenticationMechanism() == null) { + updateUserWithHashedPwd(originalUser, pwd); + } + addOrUpdateUser(originalUser); + } catch (EntityNotFoundException e) { + // TODO: Not the best way ! :( + User user = user(username, domain, username).withIsAdmin(true).withIsEmailVerified(true); + updateUserWithHashedPwd(user, pwd); + addOrUpdateUser(user); + EmailUtil.sendInviteMailToAdmin(user, pwd); + } + } + + public static void updateUserWithHashedPwd(User user, String pwd) { + String hashedPwd = BCrypt.withDefaults().hashToString(12, pwd.toCharArray()); + user.setAuthenticationMechanism( + new AuthenticationMechanism() + .withAuthType(AuthenticationMechanism.AuthType.BASIC) + .withConfig(new BasicAuthMechanism().withPassword(hashedPwd))); + } + + public static void addUsers(Set users, String domain, Boolean isAdmin) { + for (String userName : users) { + User user = user(userName, domain, userName).withIsAdmin(isAdmin); + addOrUpdateUser(user); + } + } + + public static User addOrUpdateUser(User user) { + EntityRepository userRepository = Entity.getEntityRepository(Entity.USER); + try { + RestUtil.PutResponse addedUser = userRepository.createOrUpdate(null, user); + // should not log the user auth details in LOGS + LOG.debug("Added user entry: {}", addedUser.getEntity().getName()); + return addedUser.getEntity(); + } catch (Exception exception) { + // In HA set up the other server may have already added the user. + LOG.debug("Caught exception", exception); + user.setAuthenticationMechanism(null); + LOG.debug("User entry: {} already exists.", user.getName()); + } + return null; + } + + public static User user(String name, String domain, String updatedBy) { + return new User() + .withId(UUID.randomUUID()) + .withName(name) + .withFullyQualifiedName(name) + .withEmail(name + "@" + domain) + .withUpdatedBy(updatedBy) + .withUpdatedAt(System.currentTimeMillis()) + .withIsBot(false); + } + + /** + * This method add auth mechanism in the following way: + * + *
    + *
  • If original user has already an authMechanism, add it to the user + *
  • Otherwise: + *
      + *
    • If airflow configuration is 'openmetadata' and server auth provider is not basic, add JWT auth + * mechanism from Airflow configuration + *
    • Otherwise: + *
        + *
      • If airflow configuration is 'basic', add JWT auth mechanism with a generated token which does not + * expire + *
      • Otherwise, add SSO auth mechanism from Airflow configuration + *
      + *
    + *
+ */ + public static User addOrUpdateBotUser(User user, OpenMetadataApplicationConfig openMetadataApplicationConfig) { + User originalUser = retrieveWithAuthMechanism(user); + // the user did not have an auth mechanism + AuthenticationMechanism authMechanism = originalUser != null ? originalUser.getAuthenticationMechanism() : null; + if (authMechanism == null) { + AuthenticationConfiguration authConfig = openMetadataApplicationConfig.getAuthenticationConfiguration(); + AirflowConfiguration airflowConfig = openMetadataApplicationConfig.getAirflowConfiguration(); + // if the auth provider is "openmetadata" in the configuration set JWT as auth mechanism + if ("openmetadata".equals(airflowConfig.getAuthProvider()) && !"basic".equals(authConfig.getProvider())) { + OpenMetadataJWTClientConfig jwtClientConfig = airflowConfig.getAuthConfig().getOpenmetadata(); + authMechanism = buildAuthMechanism(JWT, buildJWTAuthMechanism(jwtClientConfig, user)); + } else { + // Otherwise, set auth mechanism from airflow configuration + // TODO: https://github.com/open-metadata/OpenMetadata/issues/7712 + if (airflowConfig.getAuthConfig() != null && !"basic".equals(authConfig.getProvider())) { + switch (authConfig.getProvider()) { + case "no-auth": + break; + case "azure": + authMechanism = + buildAuthMechanism(SSO, buildAuthMechanismConfig(AZURE, airflowConfig.getAuthConfig().getAzure())); + break; + case "google": + authMechanism = + buildAuthMechanism(SSO, buildAuthMechanismConfig(GOOGLE, airflowConfig.getAuthConfig().getGoogle())); + break; + case "okta": + authMechanism = + buildAuthMechanism(SSO, buildAuthMechanismConfig(OKTA, airflowConfig.getAuthConfig().getOkta())); + break; + case "auth0": + authMechanism = + buildAuthMechanism(SSO, buildAuthMechanismConfig(AUTH_0, airflowConfig.getAuthConfig().getAuth0())); + break; + case "custom-oidc": + authMechanism = + buildAuthMechanism( + SSO, buildAuthMechanismConfig(CUSTOM_OIDC, airflowConfig.getAuthConfig().getCustomOidc())); + break; + default: + throw new IllegalArgumentException( + String.format( + "Unexpected auth provider [%s] for bot [%s]", authConfig.getProvider(), user.getName())); + } + } else if ("basic".equals(authConfig.getProvider())) { + authMechanism = buildAuthMechanism(JWT, buildJWTAuthMechanism(null, user)); + } + } + } + user.setAuthenticationMechanism(authMechanism); + user.setDescription(user.getDescription()); + user.setDisplayName(user.getDisplayName()); + user.setUpdatedBy(ADMIN_USER_NAME); + return UserUtil.addOrUpdateUser(user); + } + + private static JWTAuthMechanism buildJWTAuthMechanism(OpenMetadataJWTClientConfig jwtClientConfig, User user) { + return Objects.isNull(jwtClientConfig) || nullOrEmpty(jwtClientConfig.getJwtToken()) + ? JWTTokenGenerator.getInstance().generateJWTToken(user, JWTTokenExpiry.Unlimited) + : new JWTAuthMechanism() + .withJWTToken(jwtClientConfig.getJwtToken()) + .withJWTTokenExpiry(JWTTokenExpiry.Unlimited); + } + + private static SSOAuthMechanism buildAuthMechanismConfig( + SSOAuthMechanism.SsoServiceType ssoServiceType, Object config) { + return new SSOAuthMechanism().withSsoServiceType(ssoServiceType).withAuthConfig(config); + } + + private static AuthenticationMechanism buildAuthMechanism(AuthenticationMechanism.AuthType authType, Object config) { + return new AuthenticationMechanism().withAuthType(authType).withConfig(config); + } + + private static User retrieveWithAuthMechanism(User user) { + EntityRepository userRepository = UserRepository.class.cast(Entity.getEntityRepository(Entity.USER)); + try { + return userRepository.getByName(null, user.getName(), new EntityUtil.Fields(List.of("authenticationMechanism"))); + } catch (IOException | EntityNotFoundException e) { + LOG.debug("Bot entity: {} does not exists.", user); + return null; + } + } +}