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