Repository: incubator-hawq Updated Branches: refs/heads/ranger afd1885bc -> f78b3e09c
HAWQ-1004. Call Ranger restful api to check privilege Project: http://git-wip-us.apache.org/repos/asf/incubator-hawq/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-hawq/commit/f78b3e09 Tree: http://git-wip-us.apache.org/repos/asf/incubator-hawq/tree/f78b3e09 Diff: http://git-wip-us.apache.org/repos/asf/incubator-hawq/diff/f78b3e09 Branch: refs/heads/ranger Commit: f78b3e09c9fbc847f65be0b2dda7fb62aa5f5627 Parents: afd1885 Author: Wen Lin <w...@pivotal.io> Authored: Thu Nov 17 17:00:06 2016 +0800 Committer: Wen Lin <w...@pivotal.io> Committed: Thu Nov 17 17:00:06 2016 +0800 ---------------------------------------------------------------------- src/backend/catalog/aclchk.c | 4 +- src/backend/libpq/Makefile | 2 +- src/backend/libpq/rangerrest.c | 343 ++++++++++++++++++++++++++++++++++++ src/backend/libpq/rangerrest.h | 39 ++++ 4 files changed, 385 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/f78b3e09/src/backend/catalog/aclchk.c ---------------------------------------------------------------------- diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 038dd69..06f20f3 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -2672,8 +2672,8 @@ pg_rangercheck(AclObjectKind objkind, Oid object_oid, Oid roleid, char* rolename = getRoleName(roleid); List* actions = getActionName(mask); bool isAll = (how == ACLMASK_ALL) ? true: false; - //parameter objkind; - //return func(objectname, rolename, actions, isAll); + + int ret = check_privilege_from_ranger(rolename, objkind, objectname, actions, isAll); if(objectname){ pfree(objectname); http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/f78b3e09/src/backend/libpq/Makefile ---------------------------------------------------------------------- diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile index 28a386f..16124c7 100644 --- a/src/backend/libpq/Makefile +++ b/src/backend/libpq/Makefile @@ -15,6 +15,6 @@ include $(top_builddir)/src/Makefile.global # be-fsstubs is here for historical reasons, probably belongs elsewhere OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \ - pqformat.o pqsignal.o sha2.o pg_sha2.o + pqformat.o pqsignal.o sha2.o pg_sha2.o rangerrest.o include $(top_srcdir)/src/backend/common.mk http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/f78b3e09/src/backend/libpq/rangerrest.c ---------------------------------------------------------------------- diff --git a/src/backend/libpq/rangerrest.c b/src/backend/libpq/rangerrest.c new file mode 100644 index 0000000..aacc4e0 --- /dev/null +++ b/src/backend/libpq/rangerrest.c @@ -0,0 +1,343 @@ +/* + * 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. + */ + +/*------------------------------------------------------------------------- + * + * rangerrest.c + * routines to interact with Ranger REST API + * + *------------------------------------------------------------------------- + */ +#include <json-c/json.h> +#include "rangerrest.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "postgres.h" +#include "utils/acl.h" +#include "nodes/pg_list.h" + +/* + * Internal buffer for libcurl context + */ +typedef struct curl_context_t +{ + CURL* curl_handle; + + char curl_error_buffer[CURL_ERROR_SIZE]; + + int curl_still_running; + + struct + { + char* buffer; + int size; + } response; + + char* last_http_reponse; +} curl_context_t; +typedef curl_context_t* CURL_HANDLE; + +RangerACLResult parse_ranger_response(char* buffer) +{ + Assert(buffer != NULL); + if (strlen(buffer) == 0) + return RANGERCHECK_UNKNOWN; + + struct json_object *response = json_tokener_parse(buffer); + struct json_object *result = json_object_object_get(response, "result"); + char* szResult = json_object_get_string(result); + elog(LOG, "parse Ranger response, result:%s.", szResult); + if (strcmp(szResult, "true") == 0) + { + return RANGERCHECK_OK; + } else { + return RANGERCHECK_NO_PRIV; + } +} + +/* + * A mapping from AclObjectKind to string + */ +char* AclObjectKindStr[] = +{ + "table", /* pg_class */ + "sequence", /* pg_sequence */ + "database", /* pg_database */ + "function", /* pg_proc */ + "operator", /* pg_operator */ + "type", /* pg_type */ + "language", /* pg_language */ + "namespace", /* pg_namespace */ + "oplass", /* pg_opclass */ + "conversion", /* pg_conversion */ + "tablespace", /* pg_tablespace */ + "filespace", /* pg_filespace */ + "filesystem", /* pg_filesystem */ + "fdw", /* pg_foreign_data_wrapper */ + "foreign_server", /* pg_foreign_server */ + "protocol", /* pg_extprotocol */ + "none" /* MUST BE LAST */ +}; + +/** + * Create a JSON object for Ranger request given some parameters. + * + * { + * "requestId": 1, + * "user": "joe", + * "groups": ["admin","us"], + * "clientIp": "123.0.0.21", + * "context": "SELECT * FROM sales", + * "access": + * [ + * { + * "resource": + * { + * "database": "finance" + * }, + * "privileges": ["connect"] + * }, + * { + * "resource": + * { + * "database": "finance", + * "schema": "us", + * "table": "sales" + * }, + * "privileges": ["select, insert"] + * } + * ] + * } + */ +json_object* create_ranger_request_json(char* user, AclObjectKind kind, char* object, + List* actions, char* how) +{ + Assert(user != NULL && object != NULL && privilege != NULL + && how != NULL); + ListCell *cell = NULL; + + elog(LOG, "build json for ranger request, user:%s, kind:%s, object:%s", + user, AclObjectKindStr[kind], object); + json_object *jrequest = json_object_new_object(); + json_object *juser = json_object_new_string(user); + + json_object *jaccess = json_object_new_array(); + json_object *jelement = json_object_new_object(); + + json_object *jresource = json_object_new_object(); + switch(kind) + { + case ACL_KIND_CLASS: + case ACL_KIND_SEQUENCE: + case ACL_KIND_PROC: + case ACL_KIND_NAMESPACE: + case ACL_KIND_LANGUAGE: + { + char *ptr = NULL; char *name = NULL; + char *first = NULL; // could be a database or protocol or tablespace + char *second = NULL; // could be a schema or language + char *third = NULL; // could be a table or sequence or function + for (int idx = 0, name = strtok_r(object, ".", &ptr); + name; + name = strtok_r(NULL, ".", &ptr), idx++) + { + if (idx == 0) + { + first = pstrdup(name); + } + else if (idx == 1) + { + second = pstrdup(name); + } + else + { + third = pstrdup(name); + } + } + + if (first != NULL) + { + json_object *jfirst = json_object_new_string(first); + json_object_object_add(jresource, "database", jfirst); + } + if (second != NULL) + { + json_object *jsecond = json_object_new_string(second); + json_object_object_add(jresource, + (kind == ACL_KIND_LANGUAGE) ? "language" : "schema", jsecond); + } + if (third != NULL) + { + json_object *jthird = json_object_new_string(third); + json_object_object_add(jresource, + (kind == ACL_KIND_CLASS) ? "table" : + (kind == ACL_KIND_SEQUENCE) ? "sequence" : "function", jthird); + } + + if (first != NULL) + pfree(first); + if (second != NULL) + pfree(second); + if (third != NULL) + pfree(third); + break; + } + case ACL_KIND_OPER: + case ACL_KIND_CONVERSION: + case ACL_KIND_DATABASE: + case ACL_KIND_TABLESPACE: + case ACL_KIND_TYPE: + case ACL_KIND_FILESYSTEM: + case ACL_KIND_FDW: + case ACL_KIND_FOREIGN_SERVER: + case ACL_KIND_EXTPROTOCOL: + { + json_object *jobject = json_object_new_string(object); + json_object_object_add(jresource, AclObjectKindStr[kind], jobject); + break; + } + default: + elog(ERROR, "unrecognized objkind: %d", (int) kind); + } + + json_object *jactions = json_object_new_array(); + foreach(cell, actions) + { + json_object* jaction = json_object_new_string((char *)cell->data.ptr_value); + json_object_array_add(jactions, jaction); + } + json_object_object_add(jelement, "resource", jresource); + json_object_object_add(jelement, "privileges", jactions); + json_object_array_add(jaccess, jelement); + + json_object_object_add(jrequest, "user", juser); + json_object_object_add(jrequest, "access", jaccess); + + return jrequest; +} + +static size_t write_callback(char *contents, size_t size, size_t nitems, + void *userp) +{ + size_t realsize = size * nitems; + CURL_HANDLE curl = (struct curl_context *) userp; + + curl->response.buffer = palloc0(realsize + 1); + memset(curl->response.buffer, 0, realsize + 1); + if (curl->response.buffer == NULL) + { + /* out of memory! */ + elog(WARNING, "not enough memory for Ranger response"); + return 0; + } + + memcpy(curl->response.buffer, contents, realsize); + curl->response.size = realsize + 1; + elog(LOG, "read from Ranger Restful API: %s", curl->response.buffer); + + return realsize; +} + +void call_ranger_rest(CURL_HANDLE curl_handle, char* request) +{ + CURLcode res; + Assert(request != NULL); + + curl_global_init(CURL_GLOBAL_ALL); + + /* init the curl session */ + curl_handle->curl_handle = curl_easy_init(); + if (curl_handle->curl_handle == NULL) + { + goto _exit; + } + /* timeout */ + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 1); + + /* specify URL to get */ + curl_easy_setopt(curl_handle->curl_handle, CURLOPT_URL, "http://localhost:8089/checkprivilege"); + /* specify format */ + // struct curl_slist *plist = curl_slist_append(NULL, "Content-Type:application/json;charset=UTF-8"); + // curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, plist); + // curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, request); + + /* send all data to this function */ + curl_easy_setopt(curl_handle->curl_handle, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl_handle->curl_handle, CURLOPT_WRITEDATA, (void *)curl_handle); + + res = curl_easy_perform(curl_handle->curl_handle); + + /* check for errors */ + if(res != CURLE_OK) + { + elog(WARNING, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + } + else + { + elog(LOG, "%lu bytes retrieved from Ranger Restful API.", + curl_handle->response.size); + } + +_exit: + /* cleanup curl stuff */ + if (curl_handle->curl_handle) + { + curl_easy_cleanup(curl_handle->curl_handle); + } + + /* we're done with libcurl, so clean it up */ + curl_global_cleanup(); +} + +/* + * Check the privilege from Ranger for one role + */ +int check_privilege_from_ranger(char* user, AclObjectKind kind, char* object, + List* actions, char* how) +{ + json_object* jrequest = create_ranger_request_json(user, kind, object, + actions, how); + + Assert(jrequest != NULL); + char* request = json_object_to_json_string(jrequest); + elog(LOG, "send JSON request to Ranger: %s", request); + Assert(request != NULL); + + struct curl_context_t curl_context; + memset(&curl_context, 0, sizeof(struct curl_context_t)); + + /* call GET method to send request*/ + call_ranger_rest(&curl_context, request); + + /* free the JSON object */ + json_object_put(jrequest); + + /* parse the JSON-format result */ + RangerACLResult ret = parse_ranger_response(curl_context.response.buffer); + + /* free response buffer */ + if (curl_context.response.buffer != NULL) + { + pfree(curl_context.response.buffer); + } + + return ret; +} + http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/f78b3e09/src/backend/libpq/rangerrest.h ---------------------------------------------------------------------- diff --git a/src/backend/libpq/rangerrest.h b/src/backend/libpq/rangerrest.h new file mode 100644 index 0000000..4b73f46 --- /dev/null +++ b/src/backend/libpq/rangerrest.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/*------------------------------------------------------------------------- + * + * rangerrest.h + * routines to interact with Ranger REST API + * + *------------------------------------------------------------------------- + */ +#ifndef RANGERREST_H +#define RANGERREST_H + +#include <curl/curl.h> + +typedef enum +{ + RANGERCHECK_OK = 0, + RANGERCHECK_NO_PRIV, + RANGERCHECK_UNKNOWN +} RangerACLResult; + +#endif