1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.thrift;
20
21
22 import java.io.UnsupportedEncodingException;
23 import java.nio.ByteBuffer;
24 import java.nio.charset.CharacterCodingException;
25 import java.nio.charset.Charset;
26 import java.nio.charset.CharsetDecoder;
27 import java.security.PrivilegedExceptionAction;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.SortedMap;
33 import java.util.TreeMap;
34
35 import javax.security.auth.Subject;
36 import javax.security.auth.login.AppConfigurationEntry;
37 import javax.security.auth.login.Configuration;
38 import javax.security.auth.login.LoginContext;
39
40 import org.apache.hadoop.hbase.thrift.generated.AlreadyExists;
41 import org.apache.hadoop.hbase.thrift.generated.ColumnDescriptor;
42 import org.apache.hadoop.hbase.thrift.generated.Hbase;
43 import org.apache.hadoop.hbase.thrift.generated.TCell;
44 import org.apache.hadoop.hbase.thrift.generated.TRowResult;
45 import org.apache.hadoop.hbase.util.Base64;
46 import org.apache.thrift.protocol.TBinaryProtocol;
47 import org.apache.thrift.protocol.TProtocol;
48 import org.apache.thrift.transport.THttpClient;
49 import org.apache.thrift.transport.TSocket;
50 import org.apache.thrift.transport.TTransport;
51 import org.ietf.jgss.GSSContext;
52 import org.ietf.jgss.GSSCredential;
53 import org.ietf.jgss.GSSException;
54 import org.ietf.jgss.GSSManager;
55 import org.ietf.jgss.GSSName;
56 import org.ietf.jgss.Oid;
57
58
59
60
61 public class HttpDoAsClient {
62
63 static protected int port;
64 static protected String host;
65 CharsetDecoder decoder = null;
66 private static boolean secure = false;
67 static protected String doAsUser = null;
68 static protected String principal = null;
69
70 public static void main(String[] args) throws Exception {
71
72 if (args.length < 3 || args.length > 4) {
73
74 System.out.println("Invalid arguments!");
75 System.out.println("Usage: HttpDoAsClient host port doAsUserName [security=true]");
76 System.exit(-1);
77 }
78
79 host = args[0];
80 port = Integer.parseInt(args[1]);
81 doAsUser = args[2];
82 if (args.length > 3) {
83 secure = Boolean.parseBoolean(args[3]);
84 principal = getSubject().getPrincipals().iterator().next().getName();
85 }
86
87 final HttpDoAsClient client = new HttpDoAsClient();
88 Subject.doAs(getSubject(),
89 new PrivilegedExceptionAction<Void>() {
90 @Override
91 public Void run() throws Exception {
92 client.run();
93 return null;
94 }
95 });
96 }
97
98 HttpDoAsClient() {
99 decoder = Charset.forName("UTF-8").newDecoder();
100 }
101
102
103 private String utf8(byte[] buf) {
104 try {
105 return decoder.decode(ByteBuffer.wrap(buf)).toString();
106 } catch (CharacterCodingException e) {
107 return "[INVALID UTF-8]";
108 }
109 }
110
111
112 private byte[] bytes(String s) {
113 try {
114 return s.getBytes("UTF-8");
115 } catch (UnsupportedEncodingException e) {
116 e.printStackTrace();
117 return null;
118 }
119 }
120
121 private void run() throws Exception {
122 TTransport transport = new TSocket(host, port);
123
124 transport.open();
125 String url = "http://" + host + ":" + port;
126 THttpClient httpClient = new THttpClient(url);
127 httpClient.open();
128 TProtocol protocol = new TBinaryProtocol(httpClient);
129 Hbase.Client client = new Hbase.Client(protocol);
130
131 byte[] t = bytes("demo_table");
132
133
134
135
136 System.out.println("scanning tables...");
137 for (ByteBuffer name : refresh(client, httpClient).getTableNames()) {
138 System.out.println(" found: " + utf8(name.array()));
139 if (utf8(name.array()).equals(utf8(t))) {
140 if (refresh(client, httpClient).isTableEnabled(name)) {
141 System.out.println(" disabling table: " + utf8(name.array()));
142 refresh(client, httpClient).disableTable(name);
143 }
144 System.out.println(" deleting table: " + utf8(name.array()));
145 refresh(client, httpClient).deleteTable(name);
146 }
147 }
148
149
150
151
152
153
154 ArrayList<ColumnDescriptor> columns = new ArrayList<ColumnDescriptor>();
155 ColumnDescriptor col;
156 col = new ColumnDescriptor();
157 col.name = ByteBuffer.wrap(bytes("entry:"));
158 col.timeToLive = Integer.MAX_VALUE;
159 col.maxVersions = 10;
160 columns.add(col);
161 col = new ColumnDescriptor();
162 col.name = ByteBuffer.wrap(bytes("unused:"));
163 col.timeToLive = Integer.MAX_VALUE;
164 columns.add(col);
165
166 System.out.println("creating table: " + utf8(t));
167 try {
168
169 refresh(client, httpClient).createTable(ByteBuffer.wrap(t), columns);
170 } catch (AlreadyExists ae) {
171 System.out.println("WARN: " + ae.message);
172 }
173
174 System.out.println("column families in " + utf8(t) + ": ");
175 Map<ByteBuffer, ColumnDescriptor> columnMap = refresh(client, httpClient)
176 .getColumnDescriptors(ByteBuffer.wrap(t));
177 for (ColumnDescriptor col2 : columnMap.values()) {
178 System.out.println(" column: " + utf8(col2.name.array()) + ", maxVer: " + Integer.toString(col2.maxVersions));
179 }
180
181 transport.close();
182 httpClient.close();
183 }
184
185 private Hbase.Client refresh(Hbase.Client client, THttpClient httpClient) {
186 httpClient.setCustomHeader("doAs", doAsUser);
187 if(secure) {
188 try {
189 httpClient.setCustomHeader("Authorization", generateTicket());
190 } catch (GSSException e) {
191 e.printStackTrace();
192 }
193 }
194 return client;
195 }
196
197 private String generateTicket() throws GSSException {
198 final GSSManager manager = GSSManager.getInstance();
199
200 Oid krb5PrincipalOid = new Oid("1.2.840.113554.1.2.2.1");
201 Oid KERB_V5_OID = new Oid("1.2.840.113554.1.2.2");
202 final GSSName clientName = manager.createName(principal,
203 krb5PrincipalOid);
204 final GSSCredential clientCred = manager.createCredential(clientName,
205 8 * 3600,
206 KERB_V5_OID,
207 GSSCredential.INITIATE_ONLY);
208
209 final GSSName serverName = manager.createName(principal, krb5PrincipalOid);
210
211 final GSSContext context = manager.createContext(serverName,
212 KERB_V5_OID,
213 clientCred,
214 GSSContext.DEFAULT_LIFETIME);
215 context.requestMutualAuth(true);
216 context.requestConf(false);
217 context.requestInteg(true);
218
219 final byte[] outToken = context.initSecContext(new byte[0], 0, 0);
220 StringBuffer outputBuffer = new StringBuffer();
221 outputBuffer.append("Negotiate ");
222 outputBuffer.append(Base64.encodeBytes(outToken).replace("\n", ""));
223 System.out.print("Ticket is: " + outputBuffer);
224 return outputBuffer.toString();
225 }
226
227 private void printVersions(ByteBuffer row, List<TCell> versions) {
228 StringBuilder rowStr = new StringBuilder();
229 for (TCell cell : versions) {
230 rowStr.append(utf8(cell.value.array()));
231 rowStr.append("; ");
232 }
233 System.out.println("row: " + utf8(row.array()) + ", values: " + rowStr);
234 }
235
236 private void printRow(TRowResult rowResult) {
237
238
239 TreeMap<String, TCell> sorted = new TreeMap<String, TCell>();
240 for (Map.Entry<ByteBuffer, TCell> column : rowResult.columns.entrySet()) {
241 sorted.put(utf8(column.getKey().array()), column.getValue());
242 }
243
244 StringBuilder rowStr = new StringBuilder();
245 for (SortedMap.Entry<String, TCell> entry : sorted.entrySet()) {
246 rowStr.append(entry.getKey());
247 rowStr.append(" => ");
248 rowStr.append(utf8(entry.getValue().value.array()));
249 rowStr.append("; ");
250 }
251 System.out.println("row: " + utf8(rowResult.row.array()) + ", cols: " + rowStr);
252 }
253
254 static Subject getSubject() throws Exception {
255 if (!secure) return new Subject();
256
257
258
259
260 LoginContext context = new LoginContext("", new Subject(), null,
261 new Configuration() {
262 @Override
263 public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
264 Map<String, String> options = new HashMap<String, String>();
265 options.put("useKeyTab", "false");
266 options.put("storeKey", "false");
267 options.put("doNotPrompt", "true");
268 options.put("useTicketCache", "true");
269 options.put("renewTGT", "true");
270 options.put("refreshKrb5Config", "true");
271 options.put("isInitiator", "true");
272 String ticketCache = System.getenv("KRB5CCNAME");
273 if (ticketCache != null) {
274 options.put("ticketCache", ticketCache);
275 }
276 options.put("debug", "true");
277
278 return new AppConfigurationEntry[]{
279 new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
280 AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
281 options)};
282 }
283 });
284 context.login();
285 return context.getSubject();
286 }
287 }