/** * 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 org.apache.tez.common.security; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.IOException; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.FileSystemTestHelper.MockFileSystem; import org.apache.hadoop.fs.FilterFileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.Text; import org.apache.hadoop.security.Credentials; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; public class TestTokenCache { private static Configuration conf; private static String renewer; @BeforeClass public static void setup() throws Exception { conf = new Configuration(); conf.set(YarnConfiguration.RM_PRINCIPAL, "mapred/host@REALM"); conf.setSocketAddr(YarnConfiguration.RM_ADDRESS, InetSocketAddress.createUnresolved("127.0.0.1", 8032)); renewer = Master.getMasterPrincipal(conf); } @Test(timeout = 5000) @SuppressWarnings("deprecation") public void testBinaryCredentials() throws Exception { String binaryTokenFile = null; try { Path TEST_ROOT_DIR = new Path("target"); binaryTokenFile = FileSystem.getLocal(conf).makeQualified( new Path(TEST_ROOT_DIR, "tokenFile")).toUri().getPath(); MockFileSystem fs1 = createFileSystemForServiceName("service1"); MockFileSystem fs2 = createFileSystemForServiceName("service2"); // get the tokens for fs1 & fs2 and write out to binary creds file Credentials creds = new Credentials(); Token<?> token1 = fs1.getDelegationToken(renewer); Token<?> token2 = fs2.getDelegationToken(renewer); creds.addToken(token1.getService(), token1); creds.addToken(token2.getService(), token2); creds.writeTokenStorageFile(new Path(binaryTokenFile), conf); Credentials newCreds = new Credentials(); TokenCache.mergeBinaryTokens(newCreds, conf, binaryTokenFile); Assert.assertTrue(newCreds.getAllTokens().size() > 0); checkTokens(creds, newCreds); } finally { if (binaryTokenFile != null) { try { FileSystem.getLocal(conf).delete(new Path(binaryTokenFile)); } catch (IOException e) { // Ignore } } } } @Test(timeout=5000) public void testObtainTokensForFileSystems() throws Exception { Path[] paths = makePaths(100, "test://dir/file"); Credentials creds = new Credentials(); Configuration conf = new Configuration(TestTokenCache.conf); conf.set("fs.test.impl", TestFileSystem.class.getName()); // Cache enabled should be invoked only once conf.setBoolean("fs.test.impl.disable.cache", false); TokenCache.obtainTokensForFileSystemsInternal(creds, paths, conf); verify(TestFileSystem.fs, times(1)).addDelegationTokens(renewer, creds); // Cache disabled should be invoked for every path. conf.setBoolean("fs.test.impl.disable.cache", true); TokenCache.obtainTokensForFileSystemsInternal(creds, paths, conf); verify(TestFileSystem.fs, times(paths.length + 1)).addDelegationTokens(renewer, creds); } private Path[] makePaths(int count, String prefix) throws Exception { Path[] ps = new Path[count]; for (int i = 0; i < count; ++i) { ps[i] = new Path(prefix + i); } return ps; } public static class TestFileSystem extends FilterFileSystem { static final FileSystem fs = mock(FileSystem.class); static { try { when(fs.getUri()).thenReturn(new URI("test:///")); } catch (URISyntaxException e) { throw new RuntimeException(e); } } public TestFileSystem() { super(fs); } @Override public Token<?>[] addDelegationTokens(String renewer, Credentials credentials) throws IOException { return fs.addDelegationTokens(renewer, credentials); } } private MockFileSystem createFileSystemForServiceName(final String service) throws IOException { MockFileSystem mockFs = new MockFileSystem(); when(mockFs.getCanonicalServiceName()).thenReturn(service); when(mockFs.getDelegationToken(any(String.class))).thenAnswer( new Answer<Token<?>>() { int unique = 0; @Override public Token<?> answer(InvocationOnMock invocation) throws Throwable { Token<?> token = new Token<TokenIdentifier>(); token.setService(new Text(service)); // use unique value so when we restore from token storage, we can // tell if it's really the same token token.setKind(new Text("token" + unique++)); return token; } }); return mockFs; } private void checkTokens(Credentials creds, Credentials newCreds) { Assert.assertEquals(creds.getAllTokens().size(), newCreds.getAllTokens().size()); for (Token<?> token : newCreds.getAllTokens()) { Token<?> credsToken = creds.getToken(token.getService()); Assert.assertTrue(credsToken != null); Assert.assertEquals(token, credsToken); } } }