http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java b/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java new file mode 100644 index 0000000..c0610a2 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java @@ -0,0 +1,127 @@ +/* + * 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.kylin.rest.service; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import org.apache.kylin.metadata.project.ProjectInstance; +import org.apache.kylin.metadata.project.ProjectManager; +import org.apache.kylin.rest.constant.Constant; +import org.apache.kylin.rest.exception.InternalErrorException; +import org.apache.kylin.rest.request.CreateProjectRequest; +import org.apache.kylin.rest.request.UpdateProjectRequest; +import org.apache.kylin.rest.security.AclPermission; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +/** + * @author xduo + * + */ +@Component("projectService") +public class ProjectService extends BasicService { + + private static final Logger logger = LoggerFactory.getLogger(ProjectService.class); + + @Autowired + private AccessService accessService; + + public ProjectInstance createProject(CreateProjectRequest projectRequest) throws IOException { + String projectName = projectRequest.getName(); + String description = projectRequest.getDescription(); + ProjectInstance currentProject = getProjectManager().getProject(projectName); + + if (currentProject != null) { + throw new InternalErrorException("The project named " + projectName + " already exists"); + } + String owner = SecurityContextHolder.getContext().getAuthentication().getName(); + ProjectInstance createdProject = getProjectManager().createProject(projectName, owner, description); + accessService.init(createdProject, AclPermission.ADMINISTRATION); + logger.debug("New project created."); + + return createdProject; + } + + @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#currentProject, 'ADMINISTRATION') or hasPermission(#currentProject, 'MANAGEMENT')") + public ProjectInstance updateProject(UpdateProjectRequest projectRequest, ProjectInstance currentProject) throws IOException { + String formerProjectName = projectRequest.getFormerProjectName(); + String newProjectName = projectRequest.getNewProjectName(); + String newDescription = projectRequest.getNewDescription(); + + if (currentProject == null) { + throw new InternalErrorException("The project named " + formerProjectName + " does not exists"); + } + + ProjectInstance updatedProject = getProjectManager().updateProject(currentProject, newProjectName, newDescription); + + logger.debug("Project updated."); + + return updatedProject; + } + + public List<ProjectInstance> listAllProjects(final Integer limit, final Integer offset) { + List<ProjectInstance> projects = getProjectManager().listAllProjects(); + + int climit = (null == limit) ? Integer.MAX_VALUE : limit; + int coffset = (null == offset) ? 0 : offset; + + if (projects.size() <= coffset) { + return Collections.emptyList(); + } + + if ((projects.size() - coffset) < climit) { + return projects.subList(coffset, projects.size()); + } + + return projects.subList(coffset, coffset + climit); + } + + @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#project, 'ADMINISTRATION') or hasPermission(#cube, 'MANAGEMENT')") + public void deleteProject(String projectName, ProjectInstance project) throws IOException { + getProjectManager().dropProject(projectName); + + accessService.clean(project, true); + } + + public boolean isTableInAnyProject(String tableName) { + for (ProjectInstance projectInstance : ProjectManager.getInstance(getConfig()).listAllProjects()) { + if (projectInstance.containsTable(tableName.toUpperCase())) { + return true; + } + } + return false; + } + + public boolean isTableInProject(String tableName, String projectName) { + ProjectInstance projectInstance = ProjectManager.getInstance(getConfig()).getProject(projectName); + if (projectInstance != null) { + if (projectInstance.containsTable(tableName.toUpperCase())) { + return true; + } + } + return false; + } + +}
http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java b/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java new file mode 100644 index 0000000..84a5c67 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/service/QueryService.java @@ -0,0 +1,517 @@ +/* + * 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.kylin.rest.service; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.sql.DataSource; + +import org.apache.calcite.avatica.ColumnMetaData.Rep; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.hbase.client.Get; +import org.apache.hadoop.hbase.client.HConnection; +import org.apache.hadoop.hbase.client.HTableInterface; +import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.client.Result; +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.common.util.Bytes; +import org.apache.kylin.cube.CubeInstance; +import org.apache.kylin.cube.CubeManager; +import org.apache.kylin.cube.cuboid.Cuboid; +import org.apache.kylin.query.relnode.OLAPContext; +import org.apache.kylin.rest.constant.Constant; +import org.apache.kylin.rest.model.ColumnMeta; +import org.apache.kylin.rest.model.Query; +import org.apache.kylin.rest.model.SelectedColumnMeta; +import org.apache.kylin.rest.model.TableMeta; +import org.apache.kylin.rest.request.PrepareSqlRequest; +import org.apache.kylin.rest.request.SQLRequest; +import org.apache.kylin.rest.response.SQLResponse; +import org.apache.kylin.rest.util.QueryUtil; +import org.apache.kylin.rest.util.Serializer; +import org.apache.kylin.storage.hbase.HBaseConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +import com.google.common.collect.Lists; + +/** + * @author xduo + */ +@Component("queryService") +public class QueryService extends BasicService { + + private static final Logger logger = LoggerFactory.getLogger(QueryService.class); + + @Autowired + private CacheService cacheService; + + public static final String USER_QUERY_FAMILY = "q"; + private static final String DEFAULT_TABLE_PREFIX = "kylin_metadata"; + private static final String USER_TABLE_NAME = "_user"; + private static final String USER_QUERY_COLUMN = "c"; + + private final Serializer<Query[]> querySerializer = new Serializer<Query[]>(Query[].class); + private final BadQueryDetector badQueryDetector = new BadQueryDetector(); + + private final String hbaseUrl; + private final String tableNameBase; + private final String userTableName; + + public QueryService() { + String metadataUrl = KylinConfig.getInstanceFromEnv().getMetadataUrl(); + // split TABLE@HBASE_URL + int cut = metadataUrl.indexOf('@'); + tableNameBase = cut < 0 ? DEFAULT_TABLE_PREFIX : metadataUrl.substring(0, cut); + hbaseUrl = cut < 0 ? metadataUrl : metadataUrl.substring(cut + 1); + userTableName = tableNameBase + USER_TABLE_NAME; + + badQueryDetector.start(); + } + + public List<TableMeta> getMetadata(String project) throws SQLException { + return getMetadata(getCubeManager(), project, true); + } + + public SQLResponse query(SQLRequest sqlRequest) throws Exception { + try { + badQueryDetector.queryStart(Thread.currentThread(), sqlRequest); + + return queryWithSqlMassage(sqlRequest); + + } finally { + badQueryDetector.queryEnd(Thread.currentThread()); + } + } + + public void saveQuery(final String creator, final Query query) throws IOException { + List<Query> queries = getQueries(creator); + queries.add(query); + Query[] queryArray = new Query[queries.size()]; + + byte[] bytes = querySerializer.serialize(queries.toArray(queryArray)); + HTableInterface htable = null; + try { + htable = HBaseConnection.get(hbaseUrl).getTable(userTableName); + Put put = new Put(Bytes.toBytes(creator)); + put.add(Bytes.toBytes(USER_QUERY_FAMILY), Bytes.toBytes(USER_QUERY_COLUMN), bytes); + + htable.put(put); + htable.flushCommits(); + } finally { + IOUtils.closeQuietly(htable); + } + } + + public void removeQuery(final String creator, final String id) throws IOException { + List<Query> queries = getQueries(creator); + Iterator<Query> queryIter = queries.iterator(); + + boolean changed = false; + while (queryIter.hasNext()) { + Query temp = queryIter.next(); + if (temp.getId().equals(id)) { + queryIter.remove(); + changed = true; + break; + } + } + + if (!changed) { + return; + } + + Query[] queryArray = new Query[queries.size()]; + byte[] bytes = querySerializer.serialize(queries.toArray(queryArray)); + HTableInterface htable = null; + try { + htable = HBaseConnection.get(hbaseUrl).getTable(userTableName); + Put put = new Put(Bytes.toBytes(creator)); + put.add(Bytes.toBytes(USER_QUERY_FAMILY), Bytes.toBytes(USER_QUERY_COLUMN), bytes); + + htable.put(put); + htable.flushCommits(); + } finally { + IOUtils.closeQuietly(htable); + } + } + + public List<Query> getQueries(final String creator) throws IOException { + if (null == creator) { + return null; + } + + List<Query> queries = new ArrayList<Query>(); + HTableInterface htable = null; + try { + HConnection conn = HBaseConnection.get(hbaseUrl); + HBaseConnection.createHTableIfNeeded(conn, userTableName, USER_QUERY_FAMILY); + + htable = conn.getTable(userTableName); + Get get = new Get(Bytes.toBytes(creator)); + get.addFamily(Bytes.toBytes(USER_QUERY_FAMILY)); + Result result = htable.get(get); + Query[] query = querySerializer.deserialize(result.getValue(Bytes.toBytes(USER_QUERY_FAMILY), Bytes.toBytes(USER_QUERY_COLUMN))); + + if (null != query) { + queries.addAll(Arrays.asList(query)); + } + } finally { + IOUtils.closeQuietly(htable); + } + + return queries; + } + + public void logQuery(final SQLRequest request, final SQLResponse response) { + final String user = SecurityContextHolder.getContext().getAuthentication().getName(); + final Set<String> realizationNames = new HashSet<String>(); + final Set<Long> cuboidIds = new HashSet<Long>(); + float duration = response.getDuration() / (float) 1000; + boolean storageCacheUsed = response.isStorageCacheUsed(); + + if (!response.isHitExceptionCache() && null != OLAPContext.getThreadLocalContexts()) { + for (OLAPContext ctx : OLAPContext.getThreadLocalContexts()) { + Cuboid cuboid = ctx.storageContext.getCuboid(); + if (cuboid != null) { + //Some queries do not involve cuboid, e.g. lookup table query + cuboidIds.add(cuboid.getId()); + } + + if (ctx.realization != null) { + String realizationName = ctx.realization.getName(); + realizationNames.add(realizationName); + } + + } + } + + int resultRowCount = 0; + if (!response.getIsException() && response.getResults() != null) { + resultRowCount = response.getResults().size(); + } + + String newLine = System.getProperty("line.separator"); + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(newLine); + stringBuilder.append("==========================[QUERY]===============================").append(newLine); + stringBuilder.append("SQL: ").append(request.getSql()).append(newLine); + stringBuilder.append("User: ").append(user).append(newLine); + stringBuilder.append("Success: ").append((null == response.getExceptionMessage())).append(newLine); + stringBuilder.append("Duration: ").append(duration).append(newLine); + stringBuilder.append("Project: ").append(request.getProject()).append(newLine); + stringBuilder.append("Realization Names: ").append(realizationNames).append(newLine); + stringBuilder.append("Cuboid Ids: ").append(cuboidIds).append(newLine); + stringBuilder.append("Total scan count: ").append(response.getTotalScanCount()).append(newLine); + stringBuilder.append("Result row count: ").append(resultRowCount).append(newLine); + stringBuilder.append("Accept Partial: ").append(request.isAcceptPartial()).append(newLine); + stringBuilder.append("Is Partial Result: ").append(response.isPartial()).append(newLine); + stringBuilder.append("Hit Exception Cache: ").append(response.isHitExceptionCache()).append(newLine); + stringBuilder.append("Storage cache used: ").append(storageCacheUsed).append(newLine); + stringBuilder.append("Message: ").append(response.getExceptionMessage()).append(newLine); + stringBuilder.append("==========================[QUERY]===============================").append(newLine); + + logger.info(stringBuilder.toString()); + } + + @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#cube, 'ADMINISTRATION') or hasPermission(#cube, 'MANAGEMENT')" + " or hasPermission(#cube, 'OPERATION') or hasPermission(#cube, 'READ')") + public void checkAuthorization(CubeInstance cube) throws AccessDeniedException { + } + + private SQLResponse queryWithSqlMassage(SQLRequest sqlRequest) throws Exception { + SQLResponse fakeResponse = QueryUtil.tableauIntercept(sqlRequest.getSql()); + if (null != fakeResponse) { + logger.debug("Return fake response, is exception? " + fakeResponse.getIsException()); + return fakeResponse; + } + + String correctedSql = QueryUtil.massageSql(sqlRequest); + if (correctedSql.equals(sqlRequest.getSql()) == false) + logger.info("The corrected query: " + correctedSql); + + // add extra parameters into olap context, like acceptPartial + Map<String, String> parameters = new HashMap<String, String>(); + parameters.put(OLAPContext.PRM_ACCEPT_PARTIAL_RESULT, String.valueOf(sqlRequest.isAcceptPartial())); + OLAPContext.setParameters(parameters); + + return execute(correctedSql, sqlRequest); + + } + + protected List<TableMeta> getMetadata(CubeManager cubeMgr, String project, boolean cubedOnly) throws SQLException { + + Connection conn = null; + ResultSet columnMeta = null; + List<TableMeta> tableMetas = null; + if (StringUtils.isBlank(project)) { + return Collections.emptyList(); + } + ResultSet JDBCTableMeta = null; + try { + DataSource dataSource = cacheService.getOLAPDataSource(project); + conn = dataSource.getConnection(); + DatabaseMetaData metaData = conn.getMetaData(); + + logger.debug("getting table metas"); + JDBCTableMeta = metaData.getTables(null, null, null, null); + + tableMetas = new LinkedList<TableMeta>(); + Map<String, TableMeta> tableMap = new HashMap<String, TableMeta>(); + while (JDBCTableMeta.next()) { + String catalogName = JDBCTableMeta.getString(1); + String schemaName = JDBCTableMeta.getString(2); + + // Not every JDBC data provider offers full 10 columns, e.g., PostgreSQL has only 5 + TableMeta tblMeta = new TableMeta(catalogName == null ? Constant.FakeCatalogName : catalogName, schemaName == null ? Constant.FakeSchemaName : schemaName, JDBCTableMeta.getString(3), JDBCTableMeta.getString(4), JDBCTableMeta.getString(5), null, null, null, null, null); + + if (!cubedOnly || getProjectManager().isExposedTable(project, schemaName + "." + tblMeta.getTABLE_NAME())) { + tableMetas.add(tblMeta); + tableMap.put(tblMeta.getTABLE_SCHEM() + "#" + tblMeta.getTABLE_NAME(), tblMeta); + } + } + + logger.debug("getting column metas"); + columnMeta = metaData.getColumns(null, null, null, null); + + while (columnMeta.next()) { + String catalogName = columnMeta.getString(1); + String schemaName = columnMeta.getString(2); + + // kylin(optiq) is not strictly following JDBC specification + ColumnMeta colmnMeta = new ColumnMeta(catalogName == null ? Constant.FakeCatalogName : catalogName, schemaName == null ? Constant.FakeSchemaName : schemaName, columnMeta.getString(3), columnMeta.getString(4), columnMeta.getInt(5), columnMeta.getString(6), columnMeta.getInt(7), getInt(columnMeta.getString(8)), columnMeta.getInt(9), columnMeta.getInt(10), columnMeta.getInt(11), columnMeta.getString(12), columnMeta.getString(13), getInt(columnMeta.getString(14)), getInt(columnMeta.getString(15)), columnMeta.getInt(16), columnMeta.getInt(17), columnMeta.getString(18), columnMeta.getString(19), columnMeta.getString(20), columnMeta.getString(21), getShort(columnMeta.getString(22)), columnMeta.getString(23)); + + if (!cubedOnly || getProjectManager().isExposedColumn(project, schemaName + "." + colmnMeta.getTABLE_NAME(), colmnMeta.getCOLUMN_NAME())) { + tableMap.get(colmnMeta.getTABLE_SCHEM() + "#" + colmnMeta.getTABLE_NAME()).addColumn(colmnMeta); + } + } + logger.debug("done column metas"); + } finally { + close(columnMeta, null, conn); + if (JDBCTableMeta != null) { + JDBCTableMeta.close(); + } + } + + return tableMetas; + } + + /** + * @param sql + * @param sqlRequest + * @return + * @throws Exception + */ + private SQLResponse execute(String sql, SQLRequest sqlRequest) throws Exception { + Connection conn = null; + Statement stat = null; + ResultSet resultSet = null; + + List<List<String>> results = Lists.newArrayList(); + List<SelectedColumnMeta> columnMetas = Lists.newArrayList(); + + try { + conn = cacheService.getOLAPDataSource(sqlRequest.getProject()).getConnection(); + + if (sqlRequest instanceof PrepareSqlRequest) { + PreparedStatement preparedState = conn.prepareStatement(sql); + + for (int i = 0; i < ((PrepareSqlRequest) sqlRequest).getParams().length; i++) { + setParam(preparedState, i + 1, ((PrepareSqlRequest) sqlRequest).getParams()[i]); + } + + resultSet = preparedState.executeQuery(); + } else { + stat = conn.createStatement(); + resultSet = stat.executeQuery(sql); + } + + ResultSetMetaData metaData = resultSet.getMetaData(); + int columnCount = metaData.getColumnCount(); + + // Fill in selected column meta + for (int i = 1; i <= columnCount; ++i) { + columnMetas.add(new SelectedColumnMeta(metaData.isAutoIncrement(i), metaData.isCaseSensitive(i), metaData.isSearchable(i), metaData.isCurrency(i), metaData.isNullable(i), metaData.isSigned(i), metaData.getColumnDisplaySize(i), metaData.getColumnLabel(i), metaData.getColumnName(i), metaData.getSchemaName(i), metaData.getCatalogName(i), metaData.getTableName(i), metaData.getPrecision(i), metaData.getScale(i), metaData.getColumnType(i), metaData.getColumnTypeName(i), metaData.isReadOnly(i), metaData.isWritable(i), metaData.isDefinitelyWritable(i))); + } + + // fill in results + while (resultSet.next()) { + List<String> oneRow = Lists.newArrayListWithCapacity(columnCount); + for (int i = 0; i < columnCount; i++) { + oneRow.add((resultSet.getString(i + 1))); + } + + results.add(oneRow); + } + } finally { + close(resultSet, stat, conn); + } + + boolean isPartialResult = false; + String cube = ""; + StringBuilder sb = new StringBuilder("Scan count for each storageContext: "); + long totalScanCount = 0; + if (OLAPContext.getThreadLocalContexts() != null) { // contexts can be null in case of 'explain plan for' + for (OLAPContext ctx : OLAPContext.getThreadLocalContexts()) { + if (ctx.realization != null) { + isPartialResult |= ctx.storageContext.isPartialResultReturned(); + cube = ctx.realization.getName(); + totalScanCount += ctx.storageContext.getTotalScanCount(); + sb.append(ctx.storageContext.getTotalScanCount() + ","); + } + } + } + logger.info(sb.toString()); + + SQLResponse response = new SQLResponse(columnMetas, results, cube, 0, false, null, isPartialResult); + response.setTotalScanCount(totalScanCount); + + return response; + } + + /** + * @param preparedState + * @param param + * @throws SQLException + */ + private void setParam(PreparedStatement preparedState, int index, PrepareSqlRequest.StateParam param) throws SQLException { + boolean isNull = (null == param.getValue()); + + Class<?> clazz; + try { + clazz = Class.forName(param.getClassName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e.getMessage(), e); + } + + Rep rep = Rep.of(clazz); + + switch (rep) { + case PRIMITIVE_CHAR: + case CHARACTER: + case STRING: + preparedState.setString(index, isNull ? null : String.valueOf(param.getValue())); + break; + case PRIMITIVE_INT: + case INTEGER: + preparedState.setInt(index, isNull ? 0 : Integer.valueOf(param.getValue())); + break; + case PRIMITIVE_SHORT: + case SHORT: + preparedState.setShort(index, isNull ? 0 : Short.valueOf(param.getValue())); + break; + case PRIMITIVE_LONG: + case LONG: + preparedState.setLong(index, isNull ? 0 : Long.valueOf(param.getValue())); + break; + case PRIMITIVE_FLOAT: + case FLOAT: + preparedState.setFloat(index, isNull ? 0 : Float.valueOf(param.getValue())); + break; + case PRIMITIVE_DOUBLE: + case DOUBLE: + preparedState.setDouble(index, isNull ? 0 : Double.valueOf(param.getValue())); + break; + case PRIMITIVE_BOOLEAN: + case BOOLEAN: + preparedState.setBoolean(index, !isNull && Boolean.parseBoolean(param.getValue())); + break; + case PRIMITIVE_BYTE: + case BYTE: + preparedState.setByte(index, isNull ? 0 : Byte.valueOf(param.getValue())); + break; + case JAVA_UTIL_DATE: + case JAVA_SQL_DATE: + preparedState.setDate(index, isNull ? null : java.sql.Date.valueOf(param.getValue())); + break; + case JAVA_SQL_TIME: + preparedState.setTime(index, isNull ? null : Time.valueOf(param.getValue())); + break; + case JAVA_SQL_TIMESTAMP: + preparedState.setTimestamp(index, isNull ? null : Timestamp.valueOf(param.getValue())); + break; + default: + preparedState.setObject(index, isNull ? null : param.getValue()); + } + } + + private int getInt(String content) { + try { + return Integer.parseInt(content); + } catch (Exception e) { + return -1; + } + } + + private short getShort(String content) { + try { + return Short.parseShort(content); + } catch (Exception e) { + return -1; + } + } + + private static void close(ResultSet resultSet, Statement stat, Connection conn) { + OLAPContext.clearParameter(); + + if (resultSet != null) + try { + resultSet.close(); + } catch (SQLException e) { + logger.error("failed to close", e); + } + if (stat != null) + try { + stat.close(); + } catch (SQLException e) { + logger.error("failed to close", e); + } + if (conn != null) + try { + conn.close(); + } catch (SQLException e) { + logger.error("failed to close", e); + } + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/service/StreamingService.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/StreamingService.java b/server-base/src/main/java/org/apache/kylin/rest/service/StreamingService.java new file mode 100644 index 0000000..e49e882 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/service/StreamingService.java @@ -0,0 +1,88 @@ +/* + * 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.kylin.rest.service; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.kylin.engine.streaming.StreamingConfig; +import org.apache.kylin.rest.constant.Constant; +import org.apache.kylin.rest.exception.InternalErrorException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PostFilter; +import org.springframework.stereotype.Component; + +@Component("streamingMgmtService") +public class StreamingService extends BasicService { + + @Autowired + private AccessService accessService; + + @PostFilter(Constant.ACCESS_POST_FILTER_READ) + public List<StreamingConfig> listAllStreamingConfigs(final String table) throws IOException { + List<StreamingConfig> streamingConfigs = new ArrayList(); + if (StringUtils.isEmpty(table)) { + streamingConfigs = getStreamingManager().listAllStreaming(); + } else { + StreamingConfig config = getStreamingManager().getConfig(table); + if (config != null) { + streamingConfigs.add(config); + } + } + + return streamingConfigs; + } + + public List<StreamingConfig> getStreamingConfigs(final String table, final Integer limit, final Integer offset) throws IOException { + + List<StreamingConfig> streamingConfigs; + streamingConfigs = listAllStreamingConfigs(table); + + if (limit == null || offset == null) { + return streamingConfigs; + } + + if ((streamingConfigs.size() - offset) < limit) { + return streamingConfigs.subList(offset, streamingConfigs.size()); + } + + return streamingConfigs.subList(offset, offset + limit); + } + + public StreamingConfig createStreamingConfig(StreamingConfig config) throws IOException { + if (getStreamingManager().getStreamingConfig(config.getName()) != null) { + throw new InternalErrorException("The streamingConfig named " + config.getName() + " already exists"); + } + StreamingConfig streamingConfig = getStreamingManager().saveStreamingConfig(config); + return streamingConfig; + } + + // @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#desc, 'ADMINISTRATION') or hasPermission(#desc, 'MANAGEMENT')") + public StreamingConfig updateStreamingConfig(StreamingConfig config) throws IOException { + return getStreamingManager().updateStreamingConfig(config); + } + + // @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#desc, 'ADMINISTRATION') or hasPermission(#desc, 'MANAGEMENT')") + public void dropStreamingConfig(StreamingConfig config) throws IOException { + getStreamingManager().removeStreamingConfig(config); + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java b/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java new file mode 100644 index 0000000..07c7c6f --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java @@ -0,0 +1,289 @@ +/* + * 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.kylin.rest.service; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.apache.commons.io.IOUtils; +import org.apache.hadoop.hbase.client.Delete; +import org.apache.hadoop.hbase.client.Get; +import org.apache.hadoop.hbase.client.HTableInterface; +import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.kylin.common.util.Bytes; +import org.apache.kylin.common.util.Pair; +import org.apache.kylin.rest.security.AclHBaseStorage; +import org.apache.kylin.rest.util.Serializer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.provisioning.UserDetailsManager; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; + +/** + */ +@Component("userService") +public class UserService implements UserDetailsManager { + + private static final String PWD_PREFIX = "PWD:"; + + private Serializer<UserGrantedAuthority[]> ugaSerializer = new Serializer<UserGrantedAuthority[]>(UserGrantedAuthority[].class); + + private String userTableName = null; + + @Autowired + protected AclHBaseStorage aclHBaseStorage; + + @PostConstruct + public void init() throws IOException { + userTableName = aclHBaseStorage.prepareHBaseTable(UserService.class); + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + HTableInterface htable = null; + try { + htable = aclHBaseStorage.getTable(userTableName); + + Get get = new Get(Bytes.toBytes(username)); + get.addFamily(Bytes.toBytes(AclHBaseStorage.USER_AUTHORITY_FAMILY)); + Result result = htable.get(get); + + User user = hbaseRowToUser(result); + if (user == null) + throw new UsernameNotFoundException("User " + username + " not found."); + + return user; + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } finally { + IOUtils.closeQuietly(htable); + } + } + + private User hbaseRowToUser(Result result) throws JsonParseException, JsonMappingException, IOException { + if (null == result || result.isEmpty()) + return null; + + String username = Bytes.toString(result.getRow()); + + byte[] valueBytes = result.getValue(Bytes.toBytes(AclHBaseStorage.USER_AUTHORITY_FAMILY), Bytes.toBytes(AclHBaseStorage.USER_AUTHORITY_COLUMN)); + UserGrantedAuthority[] deserialized = ugaSerializer.deserialize(valueBytes); + + String password = ""; + List<UserGrantedAuthority> authorities = Collections.emptyList(); + + // password is stored at [0] of authorities for backward compatibility + if (deserialized != null) { + if (deserialized.length > 0 && deserialized[0].getAuthority().startsWith(PWD_PREFIX)) { + password = deserialized[0].getAuthority().substring(PWD_PREFIX.length()); + authorities = Arrays.asList(deserialized).subList(1, deserialized.length); + } else { + authorities = Arrays.asList(deserialized); + } + } + + return new User(username, password, authorities); + } + + private Pair<byte[], byte[]> userToHBaseRow(UserDetails user) throws JsonProcessingException { + byte[] key = Bytes.toBytes(user.getUsername()); + + Collection<? extends GrantedAuthority> authorities = user.getAuthorities(); + if (authorities == null) + authorities = Collections.emptyList(); + + UserGrantedAuthority[] serializing = new UserGrantedAuthority[authorities.size() + 1]; + + // password is stored as the [0] authority + serializing[0] = new UserGrantedAuthority(PWD_PREFIX + user.getPassword()); + int i = 1; + for (GrantedAuthority a : authorities) { + serializing[i++] = new UserGrantedAuthority(a.getAuthority()); + } + + byte[] value = ugaSerializer.serialize(serializing); + return Pair.newPair(key, value); + } + + @Override + public void createUser(UserDetails user) { + updateUser(user); + } + + @Override + public void updateUser(UserDetails user) { + HTableInterface htable = null; + try { + htable = aclHBaseStorage.getTable(userTableName); + + Pair<byte[], byte[]> pair = userToHBaseRow(user); + Put put = new Put(pair.getKey()); + put.add(Bytes.toBytes(AclHBaseStorage.USER_AUTHORITY_FAMILY), Bytes.toBytes(AclHBaseStorage.USER_AUTHORITY_COLUMN), pair.getSecond()); + + htable.put(put); + htable.flushCommits(); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } finally { + IOUtils.closeQuietly(htable); + } + } + + @Override + public void deleteUser(String username) { + HTableInterface htable = null; + try { + htable = aclHBaseStorage.getTable(userTableName); + + Delete delete = new Delete(Bytes.toBytes(username)); + + htable.delete(delete); + htable.flushCommits(); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } finally { + IOUtils.closeQuietly(htable); + } + } + + @Override + public void changePassword(String oldPassword, String newPassword) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean userExists(String username) { + HTableInterface htable = null; + try { + htable = aclHBaseStorage.getTable(userTableName); + + Result result = htable.get(new Get(Bytes.toBytes(username))); + + return null != result && !result.isEmpty(); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } finally { + IOUtils.closeQuietly(htable); + } + } + + public List<String> listUserAuthorities() { + List<String> all = new ArrayList<String>(); + for (UserDetails user : listUsers()) { + for (GrantedAuthority auth : user.getAuthorities()) { + if (!all.contains(auth.getAuthority())) { + all.add(auth.getAuthority()); + } + } + } + return all; + } + + public List<UserDetails> listUsers() { + Scan s = new Scan(); + s.addColumn(Bytes.toBytes(AclHBaseStorage.USER_AUTHORITY_FAMILY), Bytes.toBytes(AclHBaseStorage.USER_AUTHORITY_COLUMN)); + + List<UserDetails> all = new ArrayList<UserDetails>(); + HTableInterface htable = null; + ResultScanner scanner = null; + try { + htable = aclHBaseStorage.getTable(userTableName); + scanner = htable.getScanner(s); + + for (Result result = scanner.next(); result != null; result = scanner.next()) { + User user = hbaseRowToUser(result); + all.add(user); + } + } catch (IOException e) { + throw new RuntimeException("Failed to scan users", e); + } finally { + IOUtils.closeQuietly(scanner); + IOUtils.closeQuietly(htable); + } + return all; + } + + public static class UserGrantedAuthority implements GrantedAuthority { + private static final long serialVersionUID = -5128905636841891058L; + private String authority; + + public UserGrantedAuthority() { + } + + public UserGrantedAuthority(String authority) { + setAuthority(authority); + } + + @Override + public String getAuthority() { + return authority; + } + + public void setAuthority(String authority) { + this.authority = authority; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((authority == null) ? 0 : authority.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + UserGrantedAuthority other = (UserGrantedAuthority) obj; + if (authority == null) { + if (other.authority != null) + return false; + } else if (!authority.equals(other.authority)) + return false; + return true; + } + + @Override + public String toString() { + return authority; + } + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/util/Log4jConfigListener.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/util/Log4jConfigListener.java b/server-base/src/main/java/org/apache/kylin/rest/util/Log4jConfigListener.java new file mode 100644 index 0000000..7e79511 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/util/Log4jConfigListener.java @@ -0,0 +1,47 @@ +/* + * 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.kylin.rest.util; + +import javax.servlet.ServletContextEvent; + +import org.apache.kylin.common.KylinConfig; + +public class Log4jConfigListener extends org.springframework.web.util.Log4jConfigListener { + + private boolean isDebugTomcat; + + public Log4jConfigListener() { + this.isDebugTomcat = KylinConfig.getInstanceFromEnv().isDevEnv(); + } + + @Override + public void contextInitialized(ServletContextEvent event) { + if (!isDebugTomcat) { + super.contextInitialized(event); + } + } + + @Override + public void contextDestroyed(ServletContextEvent event) { + if (!isDebugTomcat) { + super.contextDestroyed(event); + } + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/util/QueryUtil.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/util/QueryUtil.java b/server-base/src/main/java/org/apache/kylin/rest/util/QueryUtil.java new file mode 100644 index 0000000..c15e5f9 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/util/QueryUtil.java @@ -0,0 +1,210 @@ +/* + * 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.kylin.rest.util; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.kylin.rest.model.SelectedColumnMeta; +import org.apache.kylin.rest.request.SQLRequest; +import org.apache.kylin.rest.response.SQLResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + */ +public class QueryUtil { + + protected static final Logger logger = LoggerFactory.getLogger(QueryUtil.class); + + private static final String S0 = "\\s*"; + private static final String S1 = "\\s"; + private static final String SM = "\\s+"; + private static final Pattern PTN_GROUP_BY = Pattern.compile(S1 + "GROUP" + SM + "BY" + S1, Pattern.CASE_INSENSITIVE); + private static final Pattern PTN_HAVING_COUNT_GREATER_THAN_ZERO = Pattern.compile(S1 + "HAVING" + SM + "[(]?" + S0 + "COUNT" + S0 + "[(]" + S0 + "1" + S0 + "[)]" + S0 + ">" + S0 + "0" + S0 + "[)]?", Pattern.CASE_INSENSITIVE); + private static final Pattern PTN_SUM_1 = Pattern.compile(S1 + "SUM" + S0 + "[(]" + S0 + "[1]" + S0 + "[)]" + S1, Pattern.CASE_INSENSITIVE); + + // private static final Pattern PTN_HAVING_ESCAPE_FUNCTION = + // Pattern.compile("\\{fn" + "(" + S0 + ")" + "\\}", + // Pattern.CASE_INSENSITIVE); + private static final Pattern PTN_HAVING_ESCAPE_FUNCTION = Pattern.compile("\\{fn" + "(.*?)" + "\\}", Pattern.CASE_INSENSITIVE); + + private static String[] tableauTestQueries = new String[] { "SELECT 1", // + "CREATE LOCAL TEMPORARY TABLE \"XTableau_B_Connect\" ( \"COL\" INTEGER ) ON COMMIT PRESERVE ROWS", // + "DROP TABLE \"XTableau_B_Connect\"", // + "SELECT \"COL\" FROM (SELECT 1 AS \"COL\") AS \"SUBQUERY\"", // + "SELECT TOP 1 \"COL\" FROM (SELECT 1 AS \"COL\") AS \"CHECKTOP\"", "SELECT \"COL\" FROM (SELECT 1 AS \"COL\") AS \"CHECKTOP\" LIMIT 1", // + "SELECT \"SUBCOL\" AS \"COL\" FROM ( SELECT 1 AS \"SUBCOL\" ) \"SUBQUERY\" GROUP BY 1", "SELECT \"SUBCOL\" AS \"COL\" FROM ( SELECT 1 AS \"SUBCOL\" ) \"SUBQUERY\" GROUP BY 2", "INSERT INTO \"XTableau_C_Connect\" SELECT * FROM (SELECT 1 AS COL) AS CHECKTEMP LIMIT 1", "DROP TABLE \"XTableau_C_Connect\"", "INSERT INTO \"XTableau_B_Connect\" SELECT * FROM (SELECT 1 AS COL) AS CHECKTEMP LIMIT 1" }; + + private static SQLResponse temp = new SQLResponse(new LinkedList<SelectedColumnMeta>() { + private static final long serialVersionUID = -8086728462624901359L; + + { + add(new SelectedColumnMeta(false, false, true, false, 2, true, 11, "COL", "COL", "", "", "", 10, 0, 4, "int4", false, true, false)); + } + }, new LinkedList<List<String>>() { + private static final long serialVersionUID = -470083340592928073L; + + { + add(new LinkedList<String>() { + private static final long serialVersionUID = -3673192785838230054L; + + { + add("1"); + } + }); + } + }, 0, false, null); + + private static SQLResponse[] fakeResponses = new SQLResponse[] { temp, new SQLResponse(null, null, 0, false, null), // + new SQLResponse(null, null, 0, false, null), // + temp, // + new SQLResponse(null, null, 0, true, "near 1 syntax error"), // + temp, // + new SQLResponse(null, null, 0, true, "group by 1????"), // + new SQLResponse(null, null, 0, true, "group by 2????"), // + new SQLResponse(null, null, 0, true, "XTableau_C_Connect not exist"), // + new SQLResponse(null, null, 0, true, "XTableau_C_Connect not exist"), new SQLResponse(null, null, 0, true, "XTableau_B_Connect not exist"), }; + + private static ArrayList<HashSet<String>> tableauTestQueriesInToken = new ArrayList<HashSet<String>>(); + + static { + for (String q : tableauTestQueries) { + HashSet<String> temp = new HashSet<String>(); + for (String token : q.split("[\r\n\t \\(\\)]")) { + temp.add(token); + } + temp.add(""); + tableauTestQueriesInToken.add(temp); + } + } + + public static String massageSql(SQLRequest sqlRequest) { + String sql = sqlRequest.getSql(); + sql = sql.trim(); + + while (sql.endsWith(";")) + sql = sql.substring(0, sql.length() - 1); + + int limit = sqlRequest.getLimit(); + if (limit > 0 && !sql.toLowerCase().contains("limit")) { + sql += ("\nLIMIT " + limit); + } + + int offset = sqlRequest.getOffset(); + if (offset > 0 && !sql.toLowerCase().contains("offset")) { + sql += ("\nOFFSET " + offset); + } + + return healSickSql(sql); + } + + // correct sick / invalid SQL + private static String healSickSql(String sql) { + Matcher m; + + // Case fn{ EXTRACT(...) } + // Use non-greedy regrex matching to remove escape functions + while (true) { + m = PTN_HAVING_ESCAPE_FUNCTION.matcher(sql); + if (!m.find()) + break; + sql = sql.substring(0, m.start()) + m.group(1) + sql.substring(m.end()); + } + + // Case: HAVING COUNT(1)>0 without Group By + // Tableau generates: SELECT SUM(1) AS "COL" FROM "VAC_SW" HAVING + // COUNT(1)>0 + m = PTN_HAVING_COUNT_GREATER_THAN_ZERO.matcher(sql); + if (m.find() && PTN_GROUP_BY.matcher(sql).find() == false) { + sql = sql.substring(0, m.start()) + " " + sql.substring(m.end()); + } + + // Case: SUM(1) + // Replace it with COUNT(1) + while (true) { + m = PTN_SUM_1.matcher(sql); + if (!m.find()) + break; + sql = sql.substring(0, m.start()) + " COUNT(1) " + sql.substring(m.end()); + } + + return sql; + } + + public static SQLResponse tableauIntercept(String sql) { + + String[] tokens = sql.split("[\r\n\t \\(\\)]"); + for (int i = 0; i < tableauTestQueries.length; ++i) { + if (isTokenWiseEqual(tokens, tableauTestQueriesInToken.get(i))) { + logger.info("Hit fake response " + i); + return fakeResponses[i]; + } + } + + return null; + } + + public static String makeErrorMsgUserFriendly(Throwable e) { + String msg = e.getMessage(); + + // pick ParseException error message if possible + Throwable cause = e; + while (cause != null) { + if (cause.getClass().getName().contains("ParseException")) { + msg = cause.getMessage(); + break; + } + cause = cause.getCause(); + } + + return makeErrorMsgUserFriendly(msg); + } + + public static String makeErrorMsgUserFriendly(String errorMsg) { + try { + // make one line + errorMsg = errorMsg.replaceAll("\\s", " "); + + // move cause to be ahead of sql, calcite creates the message pattern below + Pattern pattern = Pattern.compile("error while executing SQL \"(.*)\":(.*)"); + Matcher matcher = pattern.matcher(errorMsg); + if (matcher.find()) { + return matcher.group(2).trim() + "\n" + "while executing SQL: \"" + matcher.group(1).trim() + "\""; + } else + return errorMsg; + } catch (Exception e) { + return errorMsg; + } + } + + private static boolean isTokenWiseEqual(String[] tokens, HashSet<String> tokenSet) { + for (String token : tokens) { + if (!tokenSet.contains(token)) { + return false; + } + } + return true; + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/main/java/org/apache/kylin/rest/util/Serializer.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/util/Serializer.java b/server-base/src/main/java/org/apache/kylin/rest/util/Serializer.java new file mode 100644 index 0000000..d726e06 --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/util/Serializer.java @@ -0,0 +1,51 @@ +/* + * 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.kylin.rest.util; + +import java.io.IOException; + +import org.apache.kylin.common.util.JsonUtil; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; + +public class Serializer<T> { + private final Class<T> type; + + public Serializer(Class<T> type) { + this.type = type; + } + + public T deserialize(byte[] value) throws JsonParseException, JsonMappingException, IOException { + if (null == value) { + return null; + } + + return JsonUtil.readValue(value, type); + } + + public byte[] serialize(T obj) throws JsonProcessingException { + if (null == obj) { + return null; + } + + return JsonUtil.writeValueAsBytes(obj); + } +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/test/java/org/apache/kylin/rest/bean/BeanTest.java ---------------------------------------------------------------------- diff --git a/server-base/src/test/java/org/apache/kylin/rest/bean/BeanTest.java b/server-base/src/test/java/org/apache/kylin/rest/bean/BeanTest.java new file mode 100644 index 0000000..03d4dad --- /dev/null +++ b/server-base/src/test/java/org/apache/kylin/rest/bean/BeanTest.java @@ -0,0 +1,74 @@ +/* + * 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.kylin.rest.bean; + +import java.beans.IntrospectionException; + +import org.apache.kylin.rest.constant.Constant; +import org.apache.kylin.rest.model.ColumnMeta; +import org.apache.kylin.rest.model.SelectedColumnMeta; +import org.apache.kylin.rest.model.TableMeta; +import org.apache.kylin.rest.request.AccessRequest; +import org.apache.kylin.rest.request.CubeRequest; +import org.apache.kylin.rest.request.JobListRequest; +import org.apache.kylin.rest.request.SQLRequest; +import org.apache.kylin.rest.response.AccessEntryResponse; +import org.apache.kylin.rest.response.SQLResponse; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author xduo + * + */ +public class BeanTest { + + @Test + public void test() { + try { + BeanValidator.validateAccssor(ColumnMeta.class, new String[0]); + BeanValidator.validateAccssor(TableMeta.class, new String[0]); + BeanValidator.validateAccssor(SelectedColumnMeta.class, new String[0]); + BeanValidator.validateAccssor(AccessRequest.class, new String[0]); + BeanValidator.validateAccssor(CubeRequest.class, new String[0]); + BeanValidator.validateAccssor(JobListRequest.class, new String[0]); + BeanValidator.validateAccssor(SQLRequest.class, new String[0]); + BeanValidator.validateAccssor(AccessEntryResponse.class, new String[0]); + BeanValidator.validateAccssor(SQLResponse.class, new String[0]); + } catch (IntrospectionException e) { + } + + new SQLResponse(null, null, null, 0, true, null); + + SelectedColumnMeta coulmnMeta = new SelectedColumnMeta(false, false, false, false, 0, false, 0, null, null, null, null, null, 0, 0, 0, null, false, false, false); + Assert.assertTrue(!coulmnMeta.isAutoIncrement()); + Assert.assertTrue(!coulmnMeta.isCaseSensitive()); + Assert.assertTrue(!coulmnMeta.isSearchable()); + Assert.assertTrue(!coulmnMeta.isCurrency()); + Assert.assertTrue(coulmnMeta.getIsNullable() == 0); + Assert.assertTrue(!coulmnMeta.isSigned()); + + Assert.assertEquals(Constant.ACCESS_HAS_ROLE_ADMIN, "hasRole('ROLE_ADMIN')"); + Assert.assertEquals(Constant.ACCESS_POST_FILTER_READ, "hasRole('ROLE_ADMIN') or hasPermission(filterObject, 'READ') or hasPermission(filterObject, 'MANAGEMENT') " + "or hasPermission(filterObject, 'OPERATION') or hasPermission(filterObject, 'ADMINISTRATION')"); + Assert.assertEquals(Constant.FakeCatalogName, "defaultCatalog"); + Assert.assertEquals(Constant.FakeSchemaName, "defaultSchema"); + Assert.assertEquals(Constant.IDENTITY_ROLE, "role"); + Assert.assertEquals(Constant.IDENTITY_USER, "user"); + } +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server-base/src/test/java/org/apache/kylin/rest/bean/BeanValidator.java ---------------------------------------------------------------------- diff --git a/server-base/src/test/java/org/apache/kylin/rest/bean/BeanValidator.java b/server-base/src/test/java/org/apache/kylin/rest/bean/BeanValidator.java new file mode 100644 index 0000000..08010e4 --- /dev/null +++ b/server-base/src/test/java/org/apache/kylin/rest/bean/BeanValidator.java @@ -0,0 +1,160 @@ +/* + * 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.kylin.rest.bean; + +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.Assert; + +/** + * @author xduo + * + */ +public class BeanValidator { + + /** + * Tests the get/set methods of the specified class. + */ + public static <T> void validateAccssor(final Class<T> clazz, final String... skipThese) throws IntrospectionException { + final PropertyDescriptor[] props = Introspector.getBeanInfo(clazz).getPropertyDescriptors(); + for (PropertyDescriptor prop : props) { + + for (String skipThis : skipThese) { + if (skipThis.equals(prop.getName())) { + continue; + } + } + + findBooleanIsMethods(clazz, prop); + + final Method getter = prop.getReadMethod(); + final Method setter = prop.getWriteMethod(); + + if (getter != null && setter != null) { + final Class<?> returnType = getter.getReturnType(); + final Class<?>[] params = setter.getParameterTypes(); + + if (params.length == 1 && params[0] == returnType) { + try { + Object value = buildValue(returnType); + + T bean = clazz.newInstance(); + + setter.invoke(bean, value); + + Assert.assertEquals(String.format("Failed while testing property %s", prop.getName()), value, getter.invoke(bean)); + + } catch (Exception ex) { + ex.printStackTrace(); + System.err.println(String.format("An exception was thrown while testing the property %s: %s", prop.getName(), ex.toString())); + } + } + } + } + } + + private static Object buildValue(Class<?> clazz) throws InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException { + + final Constructor<?>[] ctrs = clazz.getConstructors(); + for (Constructor<?> ctr : ctrs) { + if (ctr.getParameterTypes().length == 0) { + return ctr.newInstance(); + } + } + + // Specific rules for common classes + if (clazz.isArray()) { + return Array.newInstance(clazz.getComponentType(), 1); + } else if (List.class.isAssignableFrom(clazz)) { + return Collections.emptyList(); + } else if (Set.class.isAssignableFrom(clazz)) { + return Collections.emptySet(); + } else if (Map.class.isAssignableFrom(clazz)) { + return Collections.emptyMap(); + } else if (clazz == String.class) { + return "TEST"; + } else if (clazz == boolean.class || clazz == Boolean.class) { + return true; + } else if (clazz == short.class || clazz == Short.class) { + return (short) 1; + } else if (clazz == int.class || clazz == Integer.class) { + return 1; + } else if (clazz == long.class || clazz == Long.class) { + return 1L; + } else if (clazz == double.class || clazz == Double.class) { + return 1.0D; + } else if (clazz == float.class || clazz == Float.class) { + return 1.0F; + } else if (clazz == char.class || clazz == Character.class) { + return 'T'; + } else if (clazz.isEnum()) { + return clazz.getEnumConstants()[0]; + } else if (clazz.isInterface()) { + return Proxy.newProxyInstance(clazz.getClassLoader(), new java.lang.Class[] { clazz }, new java.lang.reflect.InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (Object.class.getMethod("equals", Object.class).equals(method)) { + return proxy == args[0]; + } + if (Object.class.getMethod("hashCode", Object.class).equals(method)) { + return Integer.valueOf(System.identityHashCode(proxy)); + } + if (Object.class.getMethod("toString", Object.class).equals(method)) { + return "Bean " + getMockedType(proxy); + } + + return null; + } + + }); + } else { + System.err.println("Unable to build an instance of class " + clazz.getName() + ", please add some code to the " + BeanValidator.class.getName() + " class to do this."); + return null; + } + } + + public static <T> void findBooleanIsMethods(Class<T> clazz, PropertyDescriptor descriptor) throws IntrospectionException { + if (descriptor.getReadMethod() == null && descriptor.getPropertyType() == Boolean.class) { + try { + PropertyDescriptor pd = new PropertyDescriptor(descriptor.getName(), clazz); + descriptor.setReadMethod(pd.getReadMethod()); + } catch (IntrospectionException e) { + } + } + } + + @SuppressWarnings("unchecked") + public static <T, V extends T> Class<T> getMockedType(final V proxy) { + if (Proxy.isProxyClass(proxy.getClass())) { + return (Class<T>) proxy.getClass().getInterfaces()[0]; + } + return (Class<T>) proxy.getClass().getSuperclass(); + } +} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server/pom.xml ---------------------------------------------------------------------- diff --git a/server/pom.xml b/server/pom.xml index 3909d79..e662909 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -39,7 +39,7 @@ <dependency> <groupId>org.apache.kylin</groupId> - <artifactId>kylin-query</artifactId> + <artifactId>kylin-server-base</artifactId> <version>${project.parent.version}</version> <exclusions> <exclusion> @@ -52,82 +52,7 @@ </exclusion> </exclusions> </dependency> - <dependency> - <groupId>org.apache.kylin</groupId> - <artifactId>kylin-storage-hbase</artifactId> - <version>${project.parent.version}</version> - </dependency> - <dependency> - <groupId>org.apache.kylin</groupId> - <artifactId>kylin-source-hive</artifactId> - <version>${project.parent.version}</version> - </dependency> - <dependency> - <groupId>org.apache.kylin</groupId> - <artifactId>kylin-source-kafka</artifactId> - <version>${project.parent.version}</version> - </dependency> - <dependency> - <groupId>org.apache.kylin</groupId> - <artifactId>kylin-tool</artifactId> - <version>${project.parent.version}</version> - </dependency> - - <!-- Test & Env --> - <dependency> - <groupId>org.apache.kylin</groupId> - <artifactId>kylin-core-common</artifactId> - <type>test-jar</type> - <scope>test</scope> - <version>${project.parent.version}</version> - </dependency> - <dependency> - <groupId>org.apache.kylin</groupId> - <artifactId>kylin-storage-hbase</artifactId> - <type>test-jar</type> - <scope>test</scope> - <version>${project.parent.version}</version> - </dependency> - <!-- depends on kylin-jdbc just for running jdbc test cases in server module --> - <dependency> - <groupId>org.apache.kylin</groupId> - <artifactId>kylin-jdbc</artifactId> - <version>${project.parent.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>xerces</groupId> - <artifactId>xercesImpl</artifactId> - </dependency> - <dependency> - <groupId>xalan</groupId> - <artifactId>xalan</artifactId> - </dependency> - - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-web</artifactId> - <version>${spring.boot.version}</version> - <exclusions> - <exclusion> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-tomcat</artifactId> - </exclusion> - <exclusion> - <groupId>org.slf4j</groupId> - <artifactId>log4j-over-slf4j</artifactId> - </exclusion> - <exclusion> - <groupId>ch.qos.logback</groupId> - <artifactId>logback-classic</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-databind</artifactId> - </dependency> <dependency> <groupId>com.thetransactioncompany</groupId> <artifactId>cors-filter</artifactId> @@ -141,92 +66,6 @@ </dependency> <dependency> - <groupId>log4j</groupId> - <artifactId>log4j</artifactId> - </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>jcl-over-slf4j</artifactId> - </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-api</artifactId> - </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-log4j12</artifactId> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-beans</artifactId> - <version>${spring.framework.version}</version> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-webmvc</artifactId> - <version>${spring.framework.version}</version> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-jdbc</artifactId> - <version>${spring.framework.version}</version> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-web</artifactId> - <version>${spring.framework.version}</version> - <type>jar</type> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-core</artifactId> - <version>${spring.framework.version}</version> - <type>jar</type> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-aop</artifactId> - <version>${spring.framework.version}</version> - <type>jar</type> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-test</artifactId> - <version>${spring.framework.version}</version> - </dependency> - - <!-- Spring Security --> - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-core</artifactId> - <version>${spring.framework.version}</version> - </dependency> - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-web</artifactId> - <version>${spring.framework.version}</version> - </dependency> - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-config</artifactId> - <version>${spring.framework.version}</version> - </dependency> - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-acl</artifactId> - <version>${spring.framework.version}</version> - </dependency> - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-ldap</artifactId> - <version>${spring.framework.version}</version> - </dependency> - <dependency> - <groupId>org.springframework.security.extensions</groupId> - <artifactId>spring-security-saml2-core</artifactId> - <version>${spring.framework.security.extensions.version}</version> - </dependency> - <dependency> <groupId>org.opensaml</groupId> <artifactId>opensaml</artifactId> <version>${opensaml.version}</version> @@ -249,35 +88,55 @@ </exclusion> </exclusions> </dependency> + <dependency> - <groupId>net.sf.ehcache</groupId> - <artifactId>ehcache</artifactId> - <version>${ehcache.version}</version> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> </dependency> <dependency> - <groupId>cglib</groupId> - <artifactId>cglib</artifactId> - <version>2.2.2</version> + <groupId>org.slf4j</groupId> + <artifactId>jcl-over-slf4j</artifactId> </dependency> <dependency> - <groupId>net.sf.supercsv</groupId> - <artifactId>super-csv</artifactId> - <version>2.1.0</version> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> </dependency> - - <!-- spring aop --> <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> - <version>1.6.11</version> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </dependency> + + <!-- Test & Env --> + <dependency> + <groupId>org.apache.kylin</groupId> + <artifactId>kylin-core-common</artifactId> + <type>test-jar</type> + <scope>test</scope> + <version>${project.parent.version}</version> </dependency> <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjweaver</artifactId> - <version>1.6.11</version> + <groupId>org.apache.kylin</groupId> + <artifactId>kylin-storage-hbase</artifactId> + <type>test-jar</type> + <scope>test</scope> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>org.apache.kylin</groupId> + <artifactId>kylin-server-base</artifactId> + <type>test-jar</type> + <scope>test</scope> + <version>${project.parent.version}</version> + </dependency> + + <!-- depends on kylin-jdbc just for running jdbc test cases in server module --> + <dependency> + <groupId>org.apache.kylin</groupId> + <artifactId>kylin-jdbc</artifactId> + <version>${project.parent.version}</version> + <scope>test</scope> </dependency> - <!-- Env & Test --> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-yarn-server-resourcemanager</artifactId> @@ -407,6 +266,7 @@ </exclusion> </exclusions> </dependency> + <!-- Tomcat Env --> <dependency> <groupId>org.apache.tomcat</groupId> @@ -426,11 +286,7 @@ <version>7.0.69</version> <scope>provided</scope> </dependency> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <scope>test</scope> - </dependency> + <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-testing-util</artifactId> @@ -470,6 +326,11 @@ <version>${jetty.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server/src/main/java/org/apache/kylin/rest/constant/Constant.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/constant/Constant.java b/server/src/main/java/org/apache/kylin/rest/constant/Constant.java deleted file mode 100644 index f068e5f..0000000 --- a/server/src/main/java/org/apache/kylin/rest/constant/Constant.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.kylin.rest.constant; - -/** - * @author xduo - * - */ -public class Constant { - - public final static String FakeSchemaName = "defaultSchema"; - public final static String FakeCatalogName = "defaultCatalog"; - - public final static String IDENTITY_USER = "user"; - public final static String IDENTITY_ROLE = "role"; - - public final static String ROLE_ADMIN = "ROLE_ADMIN"; - public final static String ROLE_MODELER = "ROLE_MODELER"; - public final static String ROLE_ANALYST = "ROLE_ANALYST"; - - public final static String ACCESS_HAS_ROLE_ADMIN = "hasRole('ROLE_ADMIN')"; - public final static String ACCESS_HAS_ROLE_MODELER = "hasRole('ROLE_MODELER')"; - - public final static String ACCESS_POST_FILTER_READ = "hasRole('ROLE_ADMIN') or hasPermission(filterObject, 'READ') or hasPermission(filterObject, 'MANAGEMENT') " + "or hasPermission(filterObject, 'OPERATION') or hasPermission(filterObject, 'ADMINISTRATION')"; - - public final static String SERVER_MODE_QUERY = "query"; - public final static String SERVER_MODE_JOB = "job"; - public final static String SERVER_MODE_ALL = "all"; - -} http://git-wip-us.apache.org/repos/asf/kylin/blob/1a124e68/server/src/main/java/org/apache/kylin/rest/controller/AccessController.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/controller/AccessController.java b/server/src/main/java/org/apache/kylin/rest/controller/AccessController.java deleted file mode 100644 index 461aa3f..0000000 --- a/server/src/main/java/org/apache/kylin/rest/controller/AccessController.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.kylin.rest.controller; - -import java.io.IOException; -import java.util.List; - -import org.apache.kylin.common.persistence.AclEntity; -import org.apache.kylin.rest.request.AccessRequest; -import org.apache.kylin.rest.response.AccessEntryResponse; -import org.apache.kylin.rest.security.AclPermissionFactory; -import org.apache.kylin.rest.service.AccessService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.acls.model.Acl; -import org.springframework.security.acls.model.Permission; -import org.springframework.security.acls.model.Sid; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; - -/** - * @author xduo - * - */ -@Controller -@RequestMapping(value = "/access") -public class AccessController extends BasicController { - - @Autowired - private AccessService accessService; - - /** - * Get access entry list of a domain object - * - * @param uuid - * @return - * @throws IOException - */ - @RequestMapping(value = "/{type}/{uuid}", method = { RequestMethod.GET }) - @ResponseBody - public List<AccessEntryResponse> getAccessEntities(@PathVariable String type, @PathVariable String uuid) { - AclEntity ae = accessService.getAclEntity(type, uuid); - Acl acl = accessService.getAcl(ae); - - return accessService.generateAceResponses(acl); - } - - /** - * Grant a new access on a domain object to a user/role - * - * @param accessRequest - */ - @RequestMapping(value = "/{type}/{uuid}", method = { RequestMethod.POST }) - @ResponseBody - public List<AccessEntryResponse> grant(@PathVariable String type, @PathVariable String uuid, @RequestBody AccessRequest accessRequest) { - AclEntity ae = accessService.getAclEntity(type, uuid); - Sid sid = accessService.getSid(accessRequest.getSid(), accessRequest.isPrincipal()); - Permission permission = AclPermissionFactory.getPermission(accessRequest.getPermission()); - Acl acl = accessService.grant(ae, permission, sid); - - return accessService.generateAceResponses(acl); - } - - /** - * Update a access on a domain object - * - * @param accessRequest - */ - @RequestMapping(value = "/{type}/{uuid}", method = { RequestMethod.PUT }) - @ResponseBody - public List<AccessEntryResponse> update(@PathVariable String type, @PathVariable String uuid, @RequestBody AccessRequest accessRequest) { - AclEntity ae = accessService.getAclEntity(type, uuid); - Permission permission = AclPermissionFactory.getPermission(accessRequest.getPermission()); - Acl acl = accessService.update(ae, accessRequest.getAccessEntryId(), permission); - - return accessService.generateAceResponses(acl); - } - - /** - * Revoke access on a domain object from a user/role - * - * @param AccessRequest - */ - @RequestMapping(value = "/{type}/{uuid}", method = { RequestMethod.DELETE }) - public List<AccessEntryResponse> revoke(@PathVariable String type, @PathVariable String uuid, AccessRequest accessRequest) { - AclEntity ae = accessService.getAclEntity(type, uuid); - Acl acl = accessService.revoke(ae, accessRequest.getAccessEntryId()); - - return accessService.generateAceResponses(acl); - } - - /** - * @param accessService - */ - public void setAccessService(AccessService accessService) { - this.accessService = accessService; - } - -}
