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.rest; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotNull; 023 024import java.io.ByteArrayInputStream; 025import java.io.IOException; 026import java.io.StringWriter; 027import java.util.Collection; 028import javax.xml.bind.JAXBContext; 029import javax.xml.bind.JAXBException; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.HBaseCommonTestingUtility; 033import org.apache.hadoop.hbase.HBaseTestingUtility; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.client.Admin; 036import org.apache.hadoop.hbase.rest.client.Client; 037import org.apache.hadoop.hbase.rest.client.Cluster; 038import org.apache.hadoop.hbase.rest.client.Response; 039import org.apache.hadoop.hbase.rest.model.ColumnSchemaModel; 040import org.apache.hadoop.hbase.rest.model.TableSchemaModel; 041import org.apache.hadoop.hbase.rest.model.TestTableSchemaModel; 042import org.apache.hadoop.hbase.testclassification.MediumTests; 043import org.apache.hadoop.hbase.testclassification.RestTests; 044import org.apache.hadoop.hbase.util.Bytes; 045import org.apache.http.Header; 046import org.apache.http.message.BasicHeader; 047import org.junit.After; 048import org.junit.AfterClass; 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; 055 056@Category({RestTests.class, MediumTests.class}) 057@RunWith(Parameterized.class) 058public class TestSchemaResource { 059 @ClassRule 060 public static final HBaseClassTestRule CLASS_RULE = 061 HBaseClassTestRule.forClass(TestSchemaResource.class); 062 063 private static String TABLE1 = "TestSchemaResource1"; 064 private static String TABLE2 = "TestSchemaResource2"; 065 066 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 067 private static final HBaseRESTTestingUtility REST_TEST_UTIL = 068 new HBaseRESTTestingUtility(); 069 private static Client client; 070 private static JAXBContext context; 071 private static Configuration conf; 072 private static TestTableSchemaModel testTableSchemaModel; 073 private static Header extraHdr = null; 074 075 private static boolean csrfEnabled = true; 076 077 @Parameterized.Parameters 078 public static Collection<Object[]> parameters() { 079 return HBaseCommonTestingUtility.BOOLEAN_PARAMETERIZED; 080 } 081 082 public TestSchemaResource(Boolean csrf) { 083 csrfEnabled = csrf; 084 } 085 086 @BeforeClass 087 public static void setUpBeforeClass() throws Exception { 088 conf = TEST_UTIL.getConfiguration(); 089 conf.setBoolean(RESTServer.REST_CSRF_ENABLED_KEY, csrfEnabled); 090 if (csrfEnabled) { 091 conf.set(RESTServer.REST_CSRF_BROWSER_USERAGENTS_REGEX_KEY, ".*"); 092 } 093 extraHdr = new BasicHeader(RESTServer.REST_CSRF_CUSTOM_HEADER_DEFAULT, ""); 094 TEST_UTIL.startMiniCluster(); 095 REST_TEST_UTIL.startServletContainer(conf); 096 client = new Client(new Cluster().add("localhost", 097 REST_TEST_UTIL.getServletPort())); 098 testTableSchemaModel = new TestTableSchemaModel(); 099 context = JAXBContext.newInstance( 100 ColumnSchemaModel.class, 101 TableSchemaModel.class); 102 } 103 104 @AfterClass 105 public static void tearDownAfterClass() throws Exception { 106 REST_TEST_UTIL.shutdownServletContainer(); 107 TEST_UTIL.shutdownMiniCluster(); 108 } 109 110 @After 111 public void tearDown() throws Exception { 112 Admin admin = TEST_UTIL.getAdmin(); 113 114 for (String table : new String[] {TABLE1, TABLE2}) { 115 TableName t = TableName.valueOf(table); 116 if (admin.tableExists(t)) { 117 admin.disableTable(t); 118 admin.deleteTable(t); 119 } 120 } 121 122 conf.set("hbase.rest.readonly", "false"); 123 } 124 125 private static byte[] toXML(TableSchemaModel model) throws JAXBException { 126 StringWriter writer = new StringWriter(); 127 context.createMarshaller().marshal(model, writer); 128 return Bytes.toBytes(writer.toString()); 129 } 130 131 private static TableSchemaModel fromXML(byte[] content) 132 throws JAXBException { 133 return (TableSchemaModel) context.createUnmarshaller() 134 .unmarshal(new ByteArrayInputStream(content)); 135 } 136 137 @Test 138 public void testTableCreateAndDeleteXML() throws IOException, JAXBException { 139 String schemaPath = "/" + TABLE1 + "/schema"; 140 TableSchemaModel model; 141 Response response; 142 143 Admin admin = TEST_UTIL.getAdmin(); 144 assertFalse("Table " + TABLE1 + " should not exist", 145 admin.tableExists(TableName.valueOf(TABLE1))); 146 147 // create the table 148 model = testTableSchemaModel.buildTestModel(TABLE1); 149 testTableSchemaModel.checkModel(model, TABLE1); 150 if (csrfEnabled) { 151 // test put operation is forbidden without custom header 152 response = client.put(schemaPath, Constants.MIMETYPE_XML, toXML(model)); 153 assertEquals(400, response.getCode()); 154 } 155 156 response = client.put(schemaPath, Constants.MIMETYPE_XML, toXML(model), extraHdr); 157 assertEquals("put failed with csrf " + (csrfEnabled ? "enabled" : "disabled"), 158 201, response.getCode()); 159 160 // recall the same put operation but in read-only mode 161 conf.set("hbase.rest.readonly", "true"); 162 response = client.put(schemaPath, Constants.MIMETYPE_XML, toXML(model), extraHdr); 163 assertEquals(403, response.getCode()); 164 165 // retrieve the schema and validate it 166 response = client.get(schemaPath, Constants.MIMETYPE_XML); 167 assertEquals(200, response.getCode()); 168 assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); 169 model = fromXML(response.getBody()); 170 testTableSchemaModel.checkModel(model, TABLE1); 171 172 // with json retrieve the schema and validate it 173 response = client.get(schemaPath, Constants.MIMETYPE_JSON); 174 assertEquals(200, response.getCode()); 175 assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type")); 176 model = testTableSchemaModel.fromJSON(Bytes.toString(response.getBody())); 177 testTableSchemaModel.checkModel(model, TABLE1); 178 179 if (csrfEnabled) { 180 // test delete schema operation is forbidden without custom header 181 response = client.delete(schemaPath); 182 assertEquals(400, response.getCode()); 183 } 184 185 // test delete schema operation is forbidden in read-only mode 186 response = client.delete(schemaPath, extraHdr); 187 assertEquals(403, response.getCode()); 188 189 // return read-only setting back to default 190 conf.set("hbase.rest.readonly", "false"); 191 192 // delete the table and make sure HBase concurs 193 response = client.delete(schemaPath, extraHdr); 194 assertEquals(200, response.getCode()); 195 assertFalse(admin.tableExists(TableName.valueOf(TABLE1))); 196 } 197 198 @Test 199 public void testTableCreateAndDeletePB() throws IOException { 200 String schemaPath = "/" + TABLE2 + "/schema"; 201 TableSchemaModel model; 202 Response response; 203 204 Admin admin = TEST_UTIL.getAdmin(); 205 assertFalse(admin.tableExists(TableName.valueOf(TABLE2))); 206 207 // create the table 208 model = testTableSchemaModel.buildTestModel(TABLE2); 209 testTableSchemaModel.checkModel(model, TABLE2); 210 211 if (csrfEnabled) { 212 // test put operation is forbidden without custom header 213 response = client.put(schemaPath, Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput()); 214 assertEquals(400, response.getCode()); 215 } 216 response = client.put(schemaPath, Constants.MIMETYPE_PROTOBUF, 217 model.createProtobufOutput(), extraHdr); 218 assertEquals("put failed with csrf " + (csrfEnabled ? "enabled" : "disabled"), 219 201, response.getCode()); 220 221 // recall the same put operation but in read-only mode 222 conf.set("hbase.rest.readonly", "true"); 223 response = client.put(schemaPath, Constants.MIMETYPE_PROTOBUF, 224 model.createProtobufOutput(), extraHdr); 225 assertNotNull(extraHdr); 226 assertEquals(403, response.getCode()); 227 228 // retrieve the schema and validate it 229 response = client.get(schemaPath, Constants.MIMETYPE_PROTOBUF); 230 assertEquals(200, response.getCode()); 231 assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type")); 232 model = new TableSchemaModel(); 233 model.getObjectFromMessage(response.getBody()); 234 testTableSchemaModel.checkModel(model, TABLE2); 235 236 // retrieve the schema and validate it with alternate pbuf type 237 response = client.get(schemaPath, Constants.MIMETYPE_PROTOBUF_IETF); 238 assertEquals(200, response.getCode()); 239 assertEquals(Constants.MIMETYPE_PROTOBUF_IETF, response.getHeader("content-type")); 240 model = new TableSchemaModel(); 241 model.getObjectFromMessage(response.getBody()); 242 testTableSchemaModel.checkModel(model, TABLE2); 243 244 if (csrfEnabled) { 245 // test delete schema operation is forbidden without custom header 246 response = client.delete(schemaPath); 247 assertEquals(400, response.getCode()); 248 } 249 250 // test delete schema operation is forbidden in read-only mode 251 response = client.delete(schemaPath, extraHdr); 252 assertEquals(403, response.getCode()); 253 254 // return read-only setting back to default 255 conf.set("hbase.rest.readonly", "false"); 256 257 // delete the table and make sure HBase concurs 258 response = client.delete(schemaPath, extraHdr); 259 assertEquals(200, response.getCode()); 260 assertFalse(admin.tableExists(TableName.valueOf(TABLE2))); 261 } 262}