openGauss-server/contrib/security_plugin/gs_policy_labels.cpp

241 lines
7.8 KiB
C++

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* gs_policy_labels.cpp
* operation functions related to labes, such as update, scan, check elements.
*
* IDENTIFICATION
* contrib/security_plugin/gs_policy_labels.cpp
*
* -------------------------------------------------------------------------
*/
#include "access/heapam.h"
#include <ctype.h>
#include "commands/dbcommands.h"
#include "catalog/namespace.h"
#include "catalog/indexing.h"
#include "gs_policy_object_types.h"
#include "gs_policy_labels.h"
#include "storage/lock/lock.h"
#include "storage/spin.h"
#include "utils/atomic.h"
#include "utils/builtins.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/lsyscache.h"
static THR_LOCAL loaded_labels *all_labels = NULL;
static pg_atomic_uint64 label_global_version = 1;
static THR_LOCAL pg_atomic_uint64 label_local_version = 0;
/*
* update_label_value
* update related label row while data resource is altered
*
* If update target is view/table/function/schema will not update, they recorded using oid. So no need update.
* For column type, update gs_policy_label when table column altered.
*/
bool update_label_value(const gs_stl::gs_string object_name, const gs_stl::gs_string new_object_name,
int object_type)
{
bool updated = false;
bool nulls[Natts_gs_policy_label] = {false};
bool replaces[Natts_gs_policy_label] = {false};
Datum values[Natts_gs_policy_label] = {0};
HeapTuple rtup = NULL;
Relation rel = NULL;
TableScanDesc scan = NULL;
/* schema and table are recorded in Oid, so they are no need to update when renamed */
if (object_type != O_COLUMN) {
return updated;
}
rel = heap_open(GsPolicyLabelRelationId, RowExclusiveLock);
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
if (scan != NULL) {
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
Form_gs_policy_label rel_data = (Form_gs_policy_label)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
if (!strcasecmp(rel_data->relcolumn.data, object_name.c_str())) {
replaces[Anum_gs_policy_label_relcolumn - 1] = true;
values[Anum_gs_policy_label_relcolumn - 1] =
DirectFunctionCall1(namein, CStringGetDatum(new_object_name.c_str()));
}
HeapTuple new_tuple = heap_modify_tuple(rtup, RelationGetDescr(rel), values, nulls, replaces);
simple_heap_update(rel, &new_tuple->t_self, new_tuple);
CatalogUpdateIndexes(rel, new_tuple);
updated = true;
}
heap_endscan(scan);
}
heap_close(rel, RowExclusiveLock);
return updated;
}
/*
* scan_policy_labels
* scan existing labels from gs_policy_label to tmp_labels
*/
bool scan_policy_labels(loaded_labels *tmp_labels)
{
Assert(tmp_labels != NULL);
tmp_labels->clear();
TableScanDesc scan = NULL;
HeapTuple rtup = NULL;
Form_gs_policy_label rel_data = NULL;
bool label_type_found = false;
Relation rel = NULL;
rel = heap_open(GsPolicyLabelRelationId, AccessShareLock);
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
/* scan each row from gs_policy_label into tmp_labels */
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
rel_data = (Form_gs_policy_label)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
PolicyLabelItem item(rel_data->fqdnnamespace,
rel_data->fqdnid,
get_privilege_object_type(rel_data->fqdntype.data),
rel_data->relcolumn.data);
const char *label_name = rel_data->labelname.data;
if (item.m_obj_type == O_UNKNOWN) {
if (strlen(label_name) > 0) {
(*tmp_labels)[label_name];
}
continue; /* empty label */
}
(*tmp_labels)[label_name][item.m_obj_type].insert(item);
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
return label_type_found;
}
bool is_label_exist(const char *name)
{
if (!strcasecmp(name, "all")) { /* any label */
return true;
}
loaded_labels *tmp = get_policy_labels();
if (tmp == NULL) {
return false;
}
return (tmp->find(name) != tmp->end());
}
loaded_labels *get_policy_labels()
{
load_policy_labels(true);
return all_labels;
}
bool load_policy_labels(bool reload)
{
if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) {
return false;
}
if (!reload) {
pg_atomic_add_fetch_u64(&label_global_version, 1);
}
if (pg_atomic_compare_exchange_u64(&label_global_version, (uint64*)&label_local_version, label_global_version)) {
/* Latest label, changes nothing */
return false;
}
/* scan to load all labels */
if (all_labels == NULL) {
all_labels = new loaded_labels;
}
scan_policy_labels(all_labels);
return true;
}
/*
* check_label_has_object
* check object is bound to labels and used in other policies.
* param 'labels' means labels used in other features.
* CheckLabelBoundPolicy is the check function provided by other features that label is of policies
*/
bool check_label_has_object(const PolicyLabelItem *object,
bool (*CheckLabelBoundPolicy)(bool, const gs_stl::gs_string),
bool column_type_is_changed,
const policy_default_str_set *labels)
{
/* Ignore checking when upgrade */
if (u_sess->attr.attr_common.IsInplaceUpgrade) {
return false;
}
Assert(CheckLabelBoundPolicy != NULL);
loaded_labels *all_labels = get_policy_labels();
if (all_labels == NULL) {
return false;
}
loaded_labels::const_iterator it = all_labels->begin();
loaded_labels::const_iterator eit = all_labels->end();
for (; it != eit; ++it) {
/* for each item of loaded existing labels, and match labels */
if (labels != NULL && labels->find(*(it->first)) == labels->end()) {
continue;
}
/* find wether object(label row) is in matched label */
typed_labels::const_iterator fit = it->second->find(object->m_obj_type);
if (fit != it->second->end()) {
if (fit->second->find(*object) != fit->second->end()) {
/* check label is bound to Policy */
if (CheckLabelBoundPolicy(column_type_is_changed, *(it->first))) {
return true;
}
} else if (object->m_obj_type == O_COLUMN) { /* verify table of column */
gs_policy_label_set::const_iterator tit, teit;
tit = fit->second->begin();
teit = fit->second->end();
for (; tit != teit; ++tit) {
if (tit->m_schema == object->m_schema && tit->m_object == object->m_object) {
return true;
}
}
}
}
}
return false;
}
void reset_policy_labels()
{
pg_atomic_exchange_u64(&label_local_version, 0);
}
void clear_thread_local_label()
{
if (all_labels != NULL) {
delete all_labels;
all_labels = NULL;
}
}