/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 gobblin.crypto; import java.util.HashMap; import java.util.Map; import java.util.Properties; import lombok.extern.slf4j.Slf4j; import gobblin.configuration.ConfigurationKeys; import gobblin.configuration.State; import gobblin.configuration.WorkUnitState; import gobblin.password.PasswordManager; import gobblin.util.ForkOperatorUtils; /** * Extract encryption related information from taskState */ @Slf4j public class EncryptionConfigParser { /** * Encryption parameters for converters * Algorithm: Encryption algorithm. Can be 'any' to let the system choose one. * keystore_path: Location for the java keystore where encryption keys can be found * keystore_password: Password the keystore is encrypted with */ static final String WRITER_ENCRYPT_PREFIX = ConfigurationKeys.WRITER_PREFIX + ".encrypt"; static final String CONVERTER_ENCRYPT_PREFIX = "converter.encrypt"; public static final String ENCRYPTION_ALGORITHM_KEY = "algorithm"; public static final String ENCRYPTION_KEYSTORE_PATH_KEY = "keystore_path"; public static final String ENCRYPTION_KEYSTORE_PASSWORD_KEY = "keystore_password"; public static final String ENCRYPTION_KEYSTORE_TYPE_KEY = "keystore_type"; public static final String ENCRYPTION_KEYSTORE_TYPE_KEY_DEFAULT = "java"; public static final String ENCRYPTION_TYPE_ANY = "any"; /** * Represents the entity we are trying to retrieve configuration for. Internally this * enum maps entity type to a configuration prefix. */ public enum EntityType { CONVERTER(CONVERTER_ENCRYPT_PREFIX), WRITER(WRITER_ENCRYPT_PREFIX); private final String configPrefix; private EntityType(String configPrefix) { this.configPrefix = configPrefix; } public String getConfigPrefix() { return configPrefix; } } /** * Retrieve encryption configuration for the branch the WorKUnitState represents * @param entityType Type of entity we are retrieving config for * @param workUnitState State for the object querying config * @return A list of encryption properties or null if encryption isn't configured */ public static Map<String, Object> getConfigForBranch(EntityType entityType, WorkUnitState workUnitState) { return getConfigForBranch(workUnitState.getJobState(), entityType.getConfigPrefix(), ForkOperatorUtils.getPropertyNameForBranch(workUnitState, "")); } /** * Retrieve encryption config for a given branch of a task * @param entityType Entity type we are retrieving config for * @param taskState State of the task * @param numBranches Number of branches overall * @param branch Branch # of the current object * @return A list of encryption properties or null if encryption isn't configured */ public static Map<String, Object> getConfigForBranch(EntityType entityType, State taskState, int numBranches, int branch) { return getConfigForBranch(taskState, entityType.getConfigPrefix(), ForkOperatorUtils.getPropertyNameForBranch("", numBranches, branch)); } private static Map<String, Object> getConfigForBranch(State taskState, String prefix, String branchSuffix) { Map<String, Object> properties = extractPropertiesForBranch(taskState.getProperties(), prefix, branchSuffix); if (properties.isEmpty()) { return null; } if (getEncryptionType(properties) == null) { log.warn("Encryption algorithm not specified; ignoring other encryption settings"); return null; } PasswordManager passwordManager = PasswordManager.getInstance(taskState); if (properties.containsKey(ENCRYPTION_KEYSTORE_PASSWORD_KEY)) { properties.put(ENCRYPTION_KEYSTORE_PASSWORD_KEY, passwordManager.readPassword((String)properties.get(ENCRYPTION_KEYSTORE_PASSWORD_KEY))); } return properties; } public static String getEncryptionType(Map<String, Object> properties) { return (String)properties.get(ENCRYPTION_ALGORITHM_KEY); } public static String getKeystorePath(Map<String, Object> properties) { return (String)properties.get(ENCRYPTION_KEYSTORE_PATH_KEY); } public static String getKeystorePassword(Map<String, Object> properties) { return (String)properties.get(ENCRYPTION_KEYSTORE_PASSWORD_KEY); } /** * Get the type of keystore to instantiate */ public static String getKeystoreType(Map<String, Object> parameters) { String type = (String)parameters.get(ENCRYPTION_KEYSTORE_TYPE_KEY); if (type == null) { type = ENCRYPTION_KEYSTORE_TYPE_KEY_DEFAULT; } return type; } /** * Extract a set of properties for a given branch, stripping out the prefix and branch * suffix. * * Eg - original output: * writer.encrypt.1 -> foo * writer.encrypt.something.1 -> bar * * will transform to * * "" -> foo * something - bar * this is very similar to ConfigUtils and typesafe config; need to figure out config story * @param properties Properties to extract data from * @param prefix Prefix to match; all other properties will be ignored * @param branchSuffix Suffix for all config properties * @return Transformed properties as described above */ private static Map<String, Object> extractPropertiesForBranch( Properties properties, String prefix, String branchSuffix) { Map<String, Object> ret = new HashMap<>(); for (Map.Entry<Object, Object> prop: properties.entrySet()) { String key = (String)prop.getKey(); if (key.startsWith(prefix) && (branchSuffix.length() == 0 || key.endsWith(branchSuffix))) { int strippedKeyStart = Math.min(key.length(), prefix.length() + 1); // filter out subkeys that don't have a '.' -- eg writer.encrypted.foo shouldn't be returned // if prefix is writer.encrypt if (strippedKeyStart != key.length() && key.charAt(strippedKeyStart - 1) != '.') { continue; } int strippedKeyEnd = Math.max(strippedKeyStart, key.length() - branchSuffix.length()); String strippedKey = key.substring(strippedKeyStart, strippedKeyEnd); ret.put(strippedKey, prop.getValue()); } } return ret; } }