Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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.util;
import static org.openmetadata.common.utils.CommonUtil.listOrEmpty;
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
import static org.openmetadata.schema.entity.teams.AuthenticationMechanism.AuthType.JWT;
import static org.openmetadata.schema.type.Include.NON_DELETED;
import static org.openmetadata.service.Entity.ADMIN_ROLE;
import static org.openmetadata.service.Entity.ADMIN_USER_NAME;
import at.favre.lib.crypto.bcrypt.BCrypt;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.json.JsonPatch;
import javax.ws.rs.core.UriInfo;
import lombok.extern.slf4j.Slf4j;
import org.openmetadata.schema.api.teams.CreateUser;
import org.openmetadata.schema.auth.BasicAuthMechanism;
import org.openmetadata.schema.auth.JWTAuthMechanism;
import org.openmetadata.schema.auth.JWTTokenExpiry;
import org.openmetadata.schema.entity.teams.AuthenticationMechanism;
import org.openmetadata.schema.entity.teams.Role;
import org.openmetadata.schema.entity.teams.User;
import org.openmetadata.schema.security.client.OpenMetadataJWTClientConfig;
import org.openmetadata.schema.services.connections.metadata.AuthProvider;
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.utils.EntityInterfaceUtil;
import org.openmetadata.service.Entity;
import org.openmetadata.service.exception.EntityNotFoundException;
import org.openmetadata.service.jdbi3.EntityRepository;
import org.openmetadata.service.jdbi3.UserRepository;
import org.openmetadata.service.security.auth.CatalogSecurityContext;
import org.openmetadata.service.security.jwt.JWTTokenGenerator;
import org.openmetadata.service.util.EntityUtil.Fields;
import org.openmetadata.service.util.RestUtil.PutResponse;
@Slf4j
public final class UserUtil {
private UserUtil() {
// Private constructor for util class
}
public static void addUsers(
AuthProvider authProvider, Set adminUsers, String domain, Boolean isAdmin) {
try {
for (String username : adminUsers) {
createOrUpdateUser(authProvider, username, domain, isAdmin);
}
} catch (Exception ex) {
LOG.error("[BootstrapUser] Encountered Exception while bootstrapping admin user", ex);
}
}
private static void createOrUpdateUser(
AuthProvider authProvider, String username, String domain, Boolean isAdmin) {
UserRepository userRepository = (UserRepository) Entity.getEntityRepository(Entity.USER);
User updatedUser = null;
try {
// Create Required Fields List
Set fieldList = new HashSet<>(userRepository.getPatchFields().getFieldList());
fieldList.add("authenticationMechanism");
// Fetch Original User, is available
User originalUser = userRepository.getByName(null, username, new Fields(fieldList));
if (Boolean.FALSE.equals(originalUser.getIsBot())
&& Boolean.FALSE.equals(originalUser.getIsAdmin())) {
updatedUser = originalUser;
// Update Auth Mechanism if not present, and send mail to the user
if (authProvider.equals(AuthProvider.BASIC)) {
if (originalUser.getAuthenticationMechanism() == null
|| originalUser.getAuthenticationMechanism().equals(new AuthenticationMechanism())) {
String randomPwd = getPassword(username);
updateUserWithHashedPwd(updatedUser, randomPwd);
EmailUtil.sendInviteMailToAdmin(updatedUser, randomPwd);
}
} else {
updatedUser.setAuthenticationMechanism(new AuthenticationMechanism());
}
// Update the specific fields isAdmin
updatedUser.setIsAdmin(isAdmin);
// user email
updatedUser.setEmail(String.format("%s@%s", username, domain));
} else {
if (Boolean.TRUE.equals(originalUser.getIsBot())) {
LOG.error(
String.format(
"You configured bot user %s in initialAdmins config. Bot user cannot be promoted to be an admin.",
originalUser.getName()));
}
}
} catch (EntityNotFoundException e) {
updatedUser = user(username, domain, username).withIsAdmin(isAdmin).withIsEmailVerified(true);
// Update Auth Mechanism if not present, and send mail to the user
if (authProvider.equals(AuthProvider.BASIC)) {
String randomPwd = getPassword(username);
updateUserWithHashedPwd(updatedUser, randomPwd);
EmailUtil.sendInviteMailToAdmin(updatedUser, randomPwd);
}
}
// Update the user
if (updatedUser != null) {
addOrUpdateUser(updatedUser);
}
}
private static String getPassword(String username) {
try {
if (Boolean.TRUE.equals(EmailUtil.getSmtpSettings().getEnableSmtpServer())
&& !ADMIN_USER_NAME.equals(username)) {
EmailUtil.testConnection();
return PasswordUtil.generateRandomPassword();
}
} catch (Exception ex) {
LOG.info("Password set to Default.");
}
return ADMIN_USER_NAME;
}
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 User addOrUpdateUser(User user) {
UserRepository userRepository = (UserRepository) Entity.getEntityRepository(Entity.USER);
try {
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);
}
return null;
}
public static User user(String name, String domain, String updatedBy) {
return getUser(
updatedBy, new CreateUser().withName(name).withEmail(name + "@" + domain).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) {
User originalUser = retrieveWithAuthMechanism(user);
AuthenticationMechanism authMechanism =
originalUser != null ? originalUser.getAuthenticationMechanism() : null;
// the user did not have an auth mechanism and auth config is present
if (authMechanism == null) {
authMechanism = buildAuthMechanism(JWT, buildJWTAuthMechanism(null, user));
}
user.setAuthenticationMechanism(authMechanism);
user.setDescription(user.getDescription());
user.setDisplayName(user.getDisplayName());
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 AuthenticationMechanism buildAuthMechanism(
AuthenticationMechanism.AuthType authType, Object config) {
return new AuthenticationMechanism().withAuthType(authType).withConfig(config);
}
private static User retrieveWithAuthMechanism(User user) {
EntityRepository userRepository =
(UserRepository) Entity.getEntityRepository(Entity.USER);
try {
return userRepository.getByName(
null, user.getName(), new Fields(Set.of("authenticationMechanism")));
} catch (EntityNotFoundException e) {
LOG.debug("Bot entity: {} does not exists.", user);
return null;
}
}
public static EntityReference getUserOrBot(String name) {
EntityReference userOrBot;
try {
userOrBot = Entity.getEntityReferenceByName(Entity.USER, name, NON_DELETED);
} catch (EntityNotFoundException e) {
userOrBot = Entity.getEntityReferenceByName(Entity.BOT, name, NON_DELETED);
}
return userOrBot;
}
public static Set getRoleListFromUser(User user) {
if (nullOrEmpty(user.getRoles())) {
return new HashSet<>();
}
return listOrEmpty(user.getRoles()).stream()
.map(EntityReference::getName)
.collect(Collectors.toSet());
}
public static List validateAndGetRolesRef(Set rolesList) {
if (nullOrEmpty(rolesList)) {
return Collections.emptyList();
}
List references = new ArrayList<>();
// Fetch the roles from the database
for (String role : rolesList) {
// Admin role is not present in the roles table, it is just a flag in the user table
if (!role.equals(ADMIN_ROLE)) {
try {
Role fetchedRole = Entity.getEntityByName(Entity.ROLE, role, "id", NON_DELETED, true);
references.add(fetchedRole.getEntityReference());
} catch (EntityNotFoundException ex) {
LOG.error("[ReSyncRoles] Role not found: {}", role, ex);
}
}
}
return references;
}
public static Set getRolesFromAuthorizationToken(
CatalogSecurityContext catalogSecurityContext) {
return catalogSecurityContext.getUserRoles();
}
public static boolean isRolesSyncNeeded(Set fromToken, Set fromDB) {
// Check if there are roles in the token that are not present in the DB
for (String role : fromToken) {
if (!fromDB.contains(role)) {
return true;
}
}
// Check if there are roles in the DB that are not present in the token
for (String role : fromDB) {
if (!fromToken.contains(role)) {
return true;
}
}
return false;
}
public static boolean reSyncUserRolesFromToken(
UriInfo uriInfo, User user, Set rolesFromToken) {
boolean syncUser = false;
User updatedUser = JsonUtils.deepCopy(user, User.class);
// Check if Admin User
if (rolesFromToken.contains(ADMIN_ROLE)) {
if (Boolean.FALSE.equals(user.getIsAdmin())) {
syncUser = true;
updatedUser.setIsAdmin(true);
}
// Remove the Admin Role from the list
rolesFromToken.remove(ADMIN_ROLE);
}
Set rolesFromUser = getRoleListFromUser(user);
// Check if roles are different
if (!nullOrEmpty(rolesFromToken) && isRolesSyncNeeded(rolesFromToken, rolesFromUser)) {
syncUser = true;
List rolesReferenceFromToken = validateAndGetRolesRef(rolesFromToken);
updatedUser.setRoles(rolesReferenceFromToken);
}
if (syncUser) {
LOG.info("Syncing User Roles for User: {}", user.getName());
JsonPatch patch = JsonUtils.getJsonPatch(user, updatedUser);
UserRepository userRepository = (UserRepository) Entity.getEntityRepository(Entity.USER);
userRepository.patch(uriInfo, user.getId(), user.getName(), patch);
// Set the updated roles to the original user
user.setRoles(updatedUser.getRoles());
}
return syncUser;
}
public static User getUser(String updatedBy, CreateUser create) {
return new User()
.withId(UUID.randomUUID())
.withName(create.getName().toLowerCase())
.withFullyQualifiedName(EntityInterfaceUtil.quoteName(create.getName().toLowerCase()))
.withEmail(create.getEmail().toLowerCase())
.withDescription(create.getDescription())
.withDisplayName(create.getDisplayName())
.withIsBot(create.getIsBot())
.withIsAdmin(create.getIsAdmin())
.withProfile(create.getProfile())
.withPersonas(create.getPersonas())
.withDefaultPersona(create.getDefaultPersona())
.withTimezone(create.getTimezone())
.withUpdatedBy(updatedBy.toLowerCase())
.withUpdatedAt(System.currentTimeMillis())
.withTeams(EntityUtil.toEntityReferences(create.getTeams(), Entity.TEAM))
.withRoles(EntityUtil.toEntityReferences(create.getRoles(), Entity.ROLE))
.withDomains(EntityUtil.getEntityReferences(Entity.DOMAIN, create.getDomains()));
}
}