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 */ 018package org.apache.hadoop.hbase.security.token; 019 020import static org.hamcrest.CoreMatchers.containsString; 021import static org.hamcrest.CoreMatchers.instanceOf; 022import static org.hamcrest.MatcherAssert.assertThat; 023import static org.junit.Assert.assertEquals; 024import static org.junit.Assert.assertThrows; 025 026import java.io.IOException; 027import java.security.PrivilegedExceptionAction; 028import java.util.Arrays; 029import java.util.Collection; 030import org.apache.hadoop.hbase.HBaseClassTestRule; 031import org.apache.hadoop.hbase.HConstants; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.hadoop.hbase.client.AsyncConnection; 034import org.apache.hadoop.hbase.client.AsyncTable; 035import org.apache.hadoop.hbase.client.Connection; 036import org.apache.hadoop.hbase.client.ConnectionFactory; 037import org.apache.hadoop.hbase.ipc.NettyRpcClient; 038import org.apache.hadoop.hbase.ipc.RpcClientFactory; 039import org.apache.hadoop.hbase.security.AccessDeniedException; 040import org.apache.hadoop.hbase.security.User; 041import org.apache.hadoop.hbase.testclassification.MediumTests; 042import org.apache.hadoop.hbase.testclassification.SecurityTests; 043import org.apache.hadoop.hbase.util.FutureUtils; 044import org.apache.hadoop.security.UserGroupInformation; 045import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod; 046import org.apache.hadoop.security.token.Token; 047import org.apache.hadoop.security.token.TokenIdentifier; 048import org.junit.Before; 049import org.junit.BeforeClass; 050import org.junit.ClassRule; 051import org.junit.Test; 052import org.junit.experimental.categories.Category; 053import org.junit.runner.RunWith; 054import org.junit.runners.Parameterized; 055import org.junit.runners.Parameterized.Parameter; 056import org.junit.runners.Parameterized.Parameters; 057 058import org.apache.hadoop.hbase.shaded.protobuf.generated.AuthenticationProtos.AuthenticationService; 059import org.apache.hadoop.hbase.shaded.protobuf.generated.AuthenticationProtos.GetAuthenticationTokenRequest; 060import org.apache.hadoop.hbase.shaded.protobuf.generated.AuthenticationProtos.GetAuthenticationTokenResponse; 061import org.apache.hadoop.hbase.shaded.protobuf.generated.AuthenticationProtos.WhoAmIRequest; 062import org.apache.hadoop.hbase.shaded.protobuf.generated.AuthenticationProtos.WhoAmIResponse; 063 064@RunWith(Parameterized.class) 065@Category({ SecurityTests.class, MediumTests.class }) 066public class TestGenerateDelegationToken extends SecureTestCluster { 067 068 @ClassRule 069 public static final HBaseClassTestRule CLASS_RULE = 070 HBaseClassTestRule.forClass(TestGenerateDelegationToken.class); 071 072 @BeforeClass 073 public static void setUp() throws Exception { 074 SecureTestCluster.setUp(); 075 try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) { 076 Token<? extends TokenIdentifier> token = ClientTokenUtil.obtainToken(conn); 077 UserGroupInformation.getCurrentUser().addToken(token); 078 } 079 } 080 081 @Parameters(name = "{index}: rpcClientImpl={0}") 082 public static Collection<Object> parameters() { 083 // Client connection supports only non-blocking RPCs (due to master registry restriction), hence 084 // we only test NettyRpcClient. 085 return Arrays.asList(new Object[] { NettyRpcClient.class.getName() }); 086 } 087 088 @Parameter 089 public String rpcClientImpl; 090 091 @Before 092 public void setUpBeforeMethod() { 093 TEST_UTIL.getConfiguration().set(RpcClientFactory.CUSTOM_RPC_CLIENT_IMPL_CONF_KEY, 094 rpcClientImpl); 095 } 096 097 private void testToken() throws Exception { 098 try (AsyncConnection conn = 099 ConnectionFactory.createAsyncConnection(TEST_UTIL.getConfiguration()).get()) { 100 AsyncTable<?> table = conn.getTable(TableName.META_TABLE_NAME); 101 WhoAmIResponse response = 102 table.<AuthenticationService.Interface, WhoAmIResponse> coprocessorService( 103 AuthenticationService::newStub, 104 (s, c, r) -> s.whoAmI(c, WhoAmIRequest.getDefaultInstance(), r), 105 HConstants.EMPTY_START_ROW).get(); 106 assertEquals(USERNAME, response.getUsername()); 107 assertEquals(AuthenticationMethod.TOKEN.name(), response.getAuthMethod()); 108 IOException ioe = 109 assertThrows(IOException.class, 110 () -> FutureUtils.get(table.<AuthenticationService.Interface, 111 GetAuthenticationTokenResponse> coprocessorService(AuthenticationService::newStub, 112 (s, c, r) -> s.getAuthenticationToken(c, 113 GetAuthenticationTokenRequest.getDefaultInstance(), r), 114 HConstants.EMPTY_START_ROW))); 115 assertThat(ioe, instanceOf(AccessDeniedException.class)); 116 assertThat(ioe.getMessage(), 117 containsString("Token generation only allowed for Kerberos authenticated clients")); 118 } 119 120 } 121 122 /** 123 * Confirm that we will use delegation token first if token and kerberos tickets are both present 124 */ 125 @Test 126 public void testTokenFirst() throws Exception { 127 testToken(); 128 } 129 130 /** 131 * Confirm that we can connect to cluster successfully when there is only token present, i.e, no 132 * kerberos ticket 133 */ 134 @Test 135 public void testOnlyToken() throws Exception { 136 User user = 137 User.createUserForTesting(TEST_UTIL.getConfiguration(), "no_krb_user", new String[0]); 138 for (Token<? extends TokenIdentifier> token : User.getCurrent().getUGI().getCredentials() 139 .getAllTokens()) { 140 user.getUGI().addToken(token); 141 } 142 user.getUGI().doAs(new PrivilegedExceptionAction<Void>() { 143 144 @Override 145 public Void run() throws Exception { 146 testToken(); 147 return null; 148 } 149 }); 150 } 151}