| /** |
| * 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.hadoop.hdfs.web; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNotSame; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Matchers.anyString; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.doThrow; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.DelegationTokenRenewer; |
| import org.apache.hadoop.fs.DelegationTokenRenewer.RenewAction; |
| import org.apache.hadoop.fs.FSDataInputStream; |
| import org.apache.hadoop.fs.FSDataOutputStream; |
| import org.apache.hadoop.fs.FileStatus; |
| import org.apache.hadoop.fs.FileSystem; |
| import org.apache.hadoop.fs.Path; |
| import org.apache.hadoop.fs.permission.FsPermission; |
| import org.apache.hadoop.io.Text; |
| import org.apache.hadoop.security.SecurityUtil; |
| import org.apache.hadoop.security.UserGroupInformation; |
| import org.apache.hadoop.security.token.Token; |
| import org.apache.hadoop.security.token.TokenIdentifier; |
| import org.apache.hadoop.util.Progressable; |
| import org.junit.Test; |
| import org.mockito.Mockito; |
| import org.mockito.internal.util.reflection.Whitebox; |
| |
| public class TestTokenAspect { |
| |
| private static class DummyFs extends FileSystem implements |
| DelegationTokenRenewer.Renewable, TokenAspect.TokenManagementDelegator { |
| |
| private static final Text TOKEN_KIND = new Text("DummyFS Token"); |
| private boolean emulateSecurityEnabled; |
| private TokenAspect<DummyFs> tokenAspect; |
| private final UserGroupInformation ugi = UserGroupInformation |
| .createUserForTesting("foo", new String[] { "bar" }); |
| private URI uri; |
| |
| @Override |
| public FSDataOutputStream append(Path f, int bufferSize, |
| Progressable progress) throws IOException { |
| return null; |
| } |
| |
| @Override |
| public void cancelDelegationToken(Token<?> token) throws IOException { |
| } |
| |
| @Override |
| public FSDataOutputStream create(Path f, FsPermission permission, |
| boolean overwrite, int bufferSize, short replication, long blockSize, |
| Progressable progress) throws IOException { |
| return null; |
| } |
| |
| @Override |
| public boolean delete(Path f, boolean recursive) throws IOException { |
| return false; |
| } |
| |
| @Override |
| public URI getCanonicalUri() { |
| return super.getCanonicalUri(); |
| } |
| |
| @Override |
| public FileStatus getFileStatus(Path f) throws IOException { |
| return null; |
| } |
| |
| @Override |
| public Token<?> getRenewToken() { |
| return null; |
| } |
| |
| @Override |
| public URI getUri() { |
| return uri; |
| } |
| |
| @Override |
| public Path getWorkingDirectory() { |
| return null; |
| } |
| |
| @Override |
| public void initialize(URI name, Configuration conf) throws IOException { |
| super.initialize(name, conf); |
| setConf(conf); |
| this.uri = URI.create(name.getScheme() + "://" + name.getAuthority()); |
| tokenAspect = new TokenAspect<DummyFs>(this, |
| SecurityUtil.buildTokenService(uri), TOKEN_KIND); |
| if (emulateSecurityEnabled || UserGroupInformation.isSecurityEnabled()) { |
| tokenAspect.initDelegationToken(ugi); |
| } |
| } |
| |
| @Override |
| public FileStatus[] listStatus(Path f) throws FileNotFoundException, |
| IOException { |
| return null; |
| } |
| |
| @Override |
| public boolean mkdirs(Path f, FsPermission permission) throws IOException { |
| return false; |
| } |
| |
| @Override |
| public FSDataInputStream open(Path f, int bufferSize) throws IOException { |
| return null; |
| } |
| |
| @Override |
| public boolean rename(Path src, Path dst) throws IOException { |
| return false; |
| } |
| |
| @Override |
| public long renewDelegationToken(Token<?> token) throws IOException { |
| return 0; |
| } |
| |
| @Override |
| public <T extends TokenIdentifier> void setDelegationToken(Token<T> token) { |
| } |
| |
| @Override |
| public void setWorkingDirectory(Path new_dir) { |
| } |
| } |
| |
| private static RenewAction<?> getActionFromTokenAspect( |
| TokenAspect<DummyFs> tokenAspect) { |
| return (RenewAction<?>) Whitebox.getInternalState(tokenAspect, "action"); |
| } |
| |
| @Test |
| public void testCachedInitialization() throws IOException, URISyntaxException { |
| Configuration conf = new Configuration(); |
| DummyFs fs = spy(new DummyFs()); |
| Token<TokenIdentifier> token = new Token<TokenIdentifier>(new byte[0], |
| new byte[0], DummyFs.TOKEN_KIND, new Text("127.0.0.1:1234")); |
| |
| doReturn(token).when(fs).getDelegationToken(anyString()); |
| doReturn(token).when(fs).getRenewToken(); |
| |
| fs.emulateSecurityEnabled = true; |
| fs.initialize(new URI("dummyfs://127.0.0.1:1234"), conf); |
| |
| fs.tokenAspect.ensureTokenInitialized(); |
| verify(fs, times(1)).getDelegationToken(null); |
| verify(fs, times(1)).setDelegationToken(token); |
| |
| // For the second iteration, the token should be cached. |
| fs.tokenAspect.ensureTokenInitialized(); |
| verify(fs, times(1)).getDelegationToken(null); |
| verify(fs, times(1)).setDelegationToken(token); |
| } |
| |
| @Test |
| public void testGetRemoteToken() throws IOException, URISyntaxException { |
| Configuration conf = new Configuration(); |
| DummyFs fs = spy(new DummyFs()); |
| Token<TokenIdentifier> token = new Token<TokenIdentifier>(new byte[0], |
| new byte[0], DummyFs.TOKEN_KIND, new Text("127.0.0.1:1234")); |
| |
| doReturn(token).when(fs).getDelegationToken(anyString()); |
| doReturn(token).when(fs).getRenewToken(); |
| |
| fs.initialize(new URI("dummyfs://127.0.0.1:1234"), conf); |
| |
| fs.tokenAspect.ensureTokenInitialized(); |
| |
| // Select a token, store and renew it |
| verify(fs).setDelegationToken(token); |
| assertNotNull(Whitebox.getInternalState(fs.tokenAspect, "dtRenewer")); |
| assertNotNull(Whitebox.getInternalState(fs.tokenAspect, "action")); |
| } |
| |
| @Test |
| public void testGetRemoteTokenFailure() throws IOException, |
| URISyntaxException { |
| Configuration conf = new Configuration(); |
| DummyFs fs = spy(new DummyFs()); |
| IOException e = new IOException(); |
| doThrow(e).when(fs).getDelegationToken(anyString()); |
| |
| fs.emulateSecurityEnabled = true; |
| fs.initialize(new URI("dummyfs://127.0.0.1:1234"), conf); |
| try { |
| fs.tokenAspect.ensureTokenInitialized(); |
| } catch (IOException exc) { |
| assertEquals(e, exc); |
| } |
| } |
| |
| @Test |
| public void testInitWithNoTokens() throws IOException, URISyntaxException { |
| Configuration conf = new Configuration(); |
| DummyFs fs = spy(new DummyFs()); |
| doReturn(null).when(fs).getDelegationToken(anyString()); |
| fs.initialize(new URI("dummyfs://127.0.0.1:1234"), conf); |
| |
| fs.tokenAspect.ensureTokenInitialized(); |
| |
| // No token will be selected. |
| verify(fs, never()).setDelegationToken( |
| Mockito.<Token<? extends TokenIdentifier>> any()); |
| } |
| |
| @Test |
| public void testInitWithUGIToken() throws IOException, URISyntaxException { |
| Configuration conf = new Configuration(); |
| DummyFs fs = spy(new DummyFs()); |
| doReturn(null).when(fs).getDelegationToken(anyString()); |
| |
| Token<TokenIdentifier> token = new Token<TokenIdentifier>(new byte[0], |
| new byte[0], DummyFs.TOKEN_KIND, new Text("127.0.0.1:1234")); |
| fs.ugi.addToken(token); |
| fs.ugi.addToken(new Token<TokenIdentifier>(new byte[0], new byte[0], |
| new Text("Other token"), new Text("127.0.0.1:8021"))); |
| assertEquals("wrong tokens in user", 2, fs.ugi.getTokens().size()); |
| |
| fs.emulateSecurityEnabled = true; |
| fs.initialize(new URI("dummyfs://127.0.0.1:1234"), conf); |
| fs.tokenAspect.ensureTokenInitialized(); |
| |
| // Select a token from ugi (not from the remote host), store it but don't |
| // renew it |
| verify(fs).setDelegationToken(token); |
| verify(fs, never()).getDelegationToken(anyString()); |
| assertNull(Whitebox.getInternalState(fs.tokenAspect, "dtRenewer")); |
| assertNull(Whitebox.getInternalState(fs.tokenAspect, "action")); |
| } |
| |
| @Test |
| public void testRenewal() throws Exception { |
| Configuration conf = new Configuration(); |
| Token<?> token1 = mock(Token.class); |
| Token<?> token2 = mock(Token.class); |
| final long renewCycle = 100; |
| DelegationTokenRenewer.renewCycle = renewCycle; |
| |
| UserGroupInformation ugi = UserGroupInformation.createUserForTesting("foo", |
| new String[] { "bar" }); |
| DummyFs fs = spy(new DummyFs()); |
| |
| doReturn(token1).doReturn(token2).when(fs).getDelegationToken(null); |
| doReturn(token1).when(fs).getRenewToken(); |
| // cause token renewer to abandon the token |
| doThrow(new IOException("renew failed")).when(token1).renew(conf); |
| doThrow(new IOException("get failed")).when(fs).addDelegationTokens(null, |
| null); |
| |
| final URI uri = new URI("dummyfs://127.0.0.1:1234"); |
| TokenAspect<DummyFs> tokenAspect = new TokenAspect<DummyFs>(fs, |
| SecurityUtil.buildTokenService(uri), DummyFs.TOKEN_KIND); |
| fs.initialize(uri, conf); |
| tokenAspect.initDelegationToken(ugi); |
| |
| // trigger token acquisition |
| tokenAspect.ensureTokenInitialized(); |
| DelegationTokenRenewer.RenewAction<?> action = getActionFromTokenAspect(tokenAspect); |
| verify(fs).setDelegationToken(token1); |
| assertTrue(action.isValid()); |
| |
| // upon renewal, token will go bad based on above stubbing |
| Thread.sleep(renewCycle * 2); |
| assertSame(action, getActionFromTokenAspect(tokenAspect)); |
| assertFalse(action.isValid()); |
| |
| // now that token is invalid, should get a new one |
| tokenAspect.ensureTokenInitialized(); |
| verify(fs, times(2)).getDelegationToken(anyString()); |
| verify(fs).setDelegationToken(token2); |
| assertNotSame(action, getActionFromTokenAspect(tokenAspect)); |
| |
| action = getActionFromTokenAspect(tokenAspect); |
| assertTrue(action.isValid()); |
| } |
| } |