001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.oozie.action.hadoop;
020
021import java.io.IOException;
022import java.util.HashMap;
023import java.util.Map;
024
025import com.google.common.annotations.VisibleForTesting;
026import org.apache.hadoop.io.Text;
027import org.apache.hadoop.security.UserGroupInformation;
028import org.apache.hadoop.security.token.Token;
029import org.apache.oozie.service.ConfigurationService;
030import org.apache.oozie.util.XLog;
031
032public class CredentialsProviderFactory {
033    public static final String CRED_KEY = "oozie.credentials.credentialclasses";
034    private static final XLog LOG = XLog.getLog(CredentialsProviderFactory.class);
035    public static final String HDFS = "hdfs";
036    public static final String YARN = "yarnRM";
037    public static final String JHS = "jhs";
038    private static CredentialsProviderFactory instance;
039    private final Map<String, Class<? extends CredentialsProvider>> providerCache;
040
041    @VisibleForTesting
042    static void destroy() {
043        instance = null;
044    }
045
046    public static CredentialsProviderFactory getInstance() throws Exception {
047        if(instance == null) {
048            instance = new CredentialsProviderFactory();
049        }
050        return instance;
051    }
052
053    private CredentialsProviderFactory() throws Exception {
054        providerCache = new HashMap<>();
055        for (String function : ConfigurationService.getStrings(CRED_KEY)) {
056            function = trim(function);
057            LOG.debug("Creating Credential class for : " + function);
058            String[] str = function.split("=");
059            if (str.length > 0) {
060                String type = str[0];
061                String classname = str[1];
062                if (classname != null) {
063                    LOG.debug("Creating Credential type : '{0}', class Name : '{1}'", type, classname);
064                    Class<?> klass = null;
065                    try {
066                        klass = Thread.currentThread().getContextClassLoader().loadClass(classname);
067                    }
068                    catch (ClassNotFoundException ex) {
069                        LOG.warn("Exception while loading the class '{0}'", classname, ex);
070                        throw ex;
071                    }
072                    providerCache.put(type, (Class<CredentialsProvider>) klass);
073                } else {
074                    LOG.warn("Credential provider class is null for '{0}', skipping", type);
075                }
076            }
077        }
078        providerCache.put(HDFS, HDFSCredentials.class);
079        providerCache.put(YARN, YarnRMCredentials.class);
080        providerCache.put(JHS, JHSCredentials.class);
081    }
082
083    static Text getUniqueAlias(Token<?> token) {
084        return new Text(String.format("%s_%s_%d", token.getKind().toString(),
085                token.getService().toString(), System.currentTimeMillis()));
086    }
087
088    /**
089     * Create Credential object
090     *
091     * @param type
092     * @return Credential object
093     * @throws Exception
094     */
095    public CredentialsProvider createCredentialsProvider(String type) throws Exception {
096        Class<? extends CredentialsProvider> providerClass = providerCache.get(type);
097        if(providerClass == null){
098            return null;
099        }
100        return providerClass.newInstance();
101    }
102
103    /**
104     * Relogs into Kerberos using the Keytab for the Oozie server user.  This should be called before attempting to get delegation
105     * tokens via {@link CredentialsProvider} implementations to ensure that the Kerberos credentials are current and won't expire
106     * too soon.
107     *
108     * @throws IOException
109     */
110    public static void ensureKerberosLogin() throws IOException {
111        LOG.debug("About to relogin from keytab");
112        UserGroupInformation.getLoginUser().checkTGTAndReloginFromKeytab();
113        LOG.debug("Relogin from keytab successful");
114    }
115
116    /**
117     * To trim string
118     *
119     * @param str
120     * @return trim string
121     */
122    public String trim(String str) {
123        if (str != null) {
124            str = str.replaceAll("\\n", "");
125            str = str.replaceAll("\\t", "");
126            str = str.trim();
127        }
128        return str;
129    }
130}