Skip to content

feat: support database_labels factor in global masking rule #16470

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/api/v1/masking_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func (m *maskingLevelEvaluator) evaluateGlobalMaskingLevelOfColumn(
"table_name": tableName,
"column_name": columnName,
"classification_level": classificationLevel,
"database_labels": databaseMessage.Metadata.Labels,
}
pass, err := evaluateMaskingRulePolicyCondition(maskingRule.Condition.Expression, maskingRuleAttributes)
if err != nil {
Expand Down
30 changes: 30 additions & 0 deletions backend/api/v1/masking_evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ func TestEvalMaskingLevelOfColumn(t *testing.T) {
ProjectID: "bytebase",
InstanceID: "neon-host",
DatabaseName: "bb",
Metadata: &storepb.DatabaseMetadata{
Labels: map[string]string{
"tenant": "bytebase",
"region": "asia",
},
},
}

defaultProjectDatabaseDataClassificationID := "2b599739-41da-4c35-a9ff-4a73c6cfe32c"
Expand Down Expand Up @@ -105,6 +111,30 @@ func TestEvalMaskingLevelOfColumn(t *testing.T) {

want: "default",
},
{
description: "Follow The Global Masking Rule",
databaseMessage: defaultDatabaseMessage,
schemaName: "hiring",
tableName: "employees",
columnName: "salary",
columnCatalog: &storepb.ColumnCatalog{
Classification: "1-1-1",
},
maskingRulePolicy: &storepb.MaskingRulePolicy{
Rules: []*storepb.MaskingRulePolicy_MaskingRule{
{
// database label hit.
Condition: &expr.Expr{Expression: `database_labels["tenant"] == "bytebase"`},
SemanticType: "default",
},
},
},
filteredMaskingExceptions: []*storepb.MaskingExceptionPolicy_MaskingException{},
dataClassification: defaultClassification,
databaseProjectDatabaseClassificationID: defaultProjectDatabaseDataClassificationID,

want: "default",
},
{
description: "Respect The Exception",
databaseMessage: defaultDatabaseMessage,
Expand Down
1 change: 1 addition & 0 deletions backend/common/cel.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ var MaskingRulePolicyCELAttributes = []cel.EnvOption{
cel.Variable("table_name", cel.StringType),
cel.Variable("column_name", cel.StringType),
cel.Variable("classification_level", cel.StringType),
cel.Variable("database_labels", cel.MapType(cel.StringType, cel.StringType)),
cel.ParserExpressionSizeLimit(celLimit),
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
:consistent-menu-width="false"
:disabled="!allowAdmin"
size="small"
style="width: auto; max-width: 7rem; min-width: 2.5rem; flex-shrink: 0"
style="width: auto; min-width: 2.5rem; flex-shrink: 0"
/>
</template>

Expand Down Expand Up @@ -43,13 +43,8 @@ const factor = computed(() => {
});

const OPERATOR_DICT = new Map([
["_==_", "=="],
["_!=_", "!="],
["_<_", "<"],
["_<=_", "≤"],
["_>=_", "≥"],
["_>_", ">"],
["@not_in", "not in"],
["_[_]", "exist key:value pair"],
]);

const options = computed(() => {
Expand All @@ -59,7 +54,7 @@ const options = computed(() => {
);

const mapOption = (op: Operator): SelectOption => {
const label = OPERATOR_DICT.get(op) ?? op.replace(/^@/g, "");
const label = OPERATOR_DICT.get(op) ?? op.replace(/[@_]/g, "");
return {
label,
value: op,
Expand Down
17 changes: 12 additions & 5 deletions frontend/src/components/ExprEditor/components/StringInput.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
<template>
<NInput
:value="value"
:placeholder="$t('cel.condition.input-value')"
:placeholder="placeholder"
:disabled="!allowAdmin"
size="small"
style="min-width: 7rem; width: auto; overflow-x: hidden"
style="min-width: 5rem; width: auto; overflow-x: hidden"
@update:value="$emit('update:value', $event)"
/>
</template>

<script lang="ts" setup>
import { NInput } from "naive-ui";
import { t } from "@/plugins/i18n";
import { useExprEditorContext } from "../context";

defineProps<{
value: string;
}>();
withDefaults(
defineProps<{
value: string;
placeholder?: string;
}>(),
{
placeholder: () => t("cel.condition.input-value"),
}
);

defineEmits<{
(event: "update:value", value: string): void;
Expand Down
41 changes: 35 additions & 6 deletions frontend/src/components/ExprEditor/components/ValueInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,35 @@
@update:value="setArrayValue($event)"
/>
</template>
<template v-if="inputType === 'KEY-VALUE-INPUT'">
<div>
<NInputGroup>
<StringInput
style="width: 5rem"
placeholder="key"
:value="getStringValue(1)"
@update:value="setStringValue($event, 1)"
/>
<StringInput
style="width: 5rem"
placeholder="value"
:value="getStringValue(2)"
@update:value="setStringValue($event, 2)"
/>
</NInputGroup>
</div>
</template>
</template>

<script lang="ts" setup>
/* eslint-disable vue/no-mutating-props */
import { isNumber } from "lodash-es";
import { NInputGroup } from "naive-ui";
import { computed, watch } from "vue";
import {
type Factor,
type ConditionExpr,
isDictionaryOperator,
isEqualityOperator,
isCollectionOperator,
isStringOperator,
Expand All @@ -61,7 +82,12 @@ import NumberInput from "./NumberInput.vue";
import SingleSelect from "./SingleSelect.vue";
import StringInput from "./StringInput.vue";

type InputType = "INPUT" | "SINGLE-SELECT" | "MULTI-SELECT" | "MULTI-INPUT";
type InputType =
| "INPUT"
| "SINGLE-SELECT"
| "MULTI-SELECT"
| "MULTI-INPUT"
| "KEY-VALUE-INPUT";

const props = defineProps<{
expr: ConditionExpr;
Expand All @@ -72,7 +98,7 @@ const operator = computed(() => {
});

const factor = computed(() => {
return props.expr.args[0];
return props.expr.args[0] as Factor;
});

const { factorSupportDropdown } = useExprEditorContext();
Expand Down Expand Up @@ -100,6 +126,9 @@ const inputType = computed((): InputType => {
? "MULTI-SELECT"
: "MULTI-INPUT";
}
if (isDictionaryOperator(operator.value)) {
return "KEY-VALUE-INPUT";
}
if (isEqualityOperator(operator.value)) {
if (factorSupportDropdown.value.includes(factor.value)) {
return "SINGLE-SELECT";
Expand All @@ -117,13 +146,13 @@ const setNumberValue = (value: number) => {
props.expr.args[1] = value;
};

const getStringValue = () => {
const value = props.expr.args[1];
const getStringValue = (index: number = 1) => {
const value = props.expr.args[index];
if (typeof value !== "string") return "";
return value;
};
const setStringValue = (value: string) => {
props.expr.args[1] = value;
const setStringValue = (value: string, index: number = 1) => {
props.expr.args[index] = value;
};

const getArrayValue = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<div class="flex items-center space-x-2">
<NInputNumber
:value="state.minLength"
:readonly="!allowEdit"
:disabled="!allowEdit"
class="w-24"
:min="1"
:placeholder="'Minimum length'"
Expand All @@ -35,7 +35,7 @@
</div>
<NCheckbox
:checked="state.requireNumber"
:readonly="!allowEdit"
:disabled="!allowEdit"
@update:checked="
(val) => {
onUpdate({ requireNumber: val });
Expand All @@ -50,7 +50,7 @@
</NCheckbox>
<NCheckbox
:checked="state.requireLetter"
:readonly="!allowEdit"
:disabled="!allowEdit"
@update:checked="
(val) => {
onUpdate({ requireLetter: val });
Expand All @@ -65,7 +65,7 @@
</NCheckbox>
<NCheckbox
:checked="state.requireUppercaseLetter"
:readonly="!allowEdit"
:disabled="!allowEdit"
@update:checked="
(val) => {
onUpdate({ requireUppercaseLetter: val });
Expand All @@ -82,7 +82,7 @@
</NCheckbox>
<NCheckbox
:checked="state.requireSpecialCharacter"
:readonly="!allowEdit"
:disabled="!allowEdit"
@update:checked="
(val) => {
onUpdate({ requireSpecialCharacter: val });
Expand All @@ -99,7 +99,7 @@
</NCheckbox>
<NCheckbox
:checked="state.requireResetPasswordForFirstLogin"
:readonly="!allowEdit"
:disabled="!allowEdit"
@update:checked="
(val) => {
onUpdate({ requireResetPasswordForFirstLogin: val });
Expand All @@ -116,7 +116,7 @@
</NCheckbox>
<NCheckbox
:checked="!!state.passwordRotation"
:readonly="!allowEdit"
:disabled="!allowEdit"
class="!flex !items-center"
@update:checked="
(checked) => {
Expand All @@ -142,7 +142,7 @@
:value="
Number(state.passwordRotation.seconds.divide(24 * 60 * 60))
"
:readonly="!allowEdit"
:disabled="!allowEdit"
:min="1"
class="w-24 mx-2"
:size="'small'"
Expand Down Expand Up @@ -180,7 +180,10 @@ import { computed, ref, reactive } from "vue";
import { featureToRef } from "@/store";
import { useSettingV1Store } from "@/store/modules/v1/setting";
import { Duration } from "@/types/proto/google/protobuf/duration";
import { PasswordRestrictionSetting, Setting_SettingName } from "@/types/proto/v1/setting_service";
import {
PasswordRestrictionSetting,
Setting_SettingName,
} from "@/types/proto/v1/setting_service";
import { FeatureBadge, FeatureModal } from "../FeatureGuard";

const DEFAULT_MIN_LENGTH = 8;
Expand All @@ -195,8 +198,9 @@ const hasPasswordFeature = featureToRef("bb.feature.password-restriction");

const passwordRestrictionSetting = computed(
() =>
settingV1Store.getSettingByName(Setting_SettingName.PASSWORD_RESTRICTION)?.value
?.passwordRestrictionSetting ?? PasswordRestrictionSetting.fromPartial({})
settingV1Store.getSettingByName(Setting_SettingName.PASSWORD_RESTRICTION)
?.value?.passwordRestrictionSetting ??
PasswordRestrictionSetting.fromPartial({})
);

const state = reactive<PasswordRestrictionSetting>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ const factorList = computed((): Factor[] => {
"table_name",
"column_name",
"classification_level",
"database_labels",
];

return list;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="space-y-4 w-full">
<div
class="flex flex-col md:flex-row items-start md:items-stretch gap-x-4 gap-y-4 overflow-hidden"
class="flex flex-col lg:flex-row items-start lg:items-stretch gap-x-4 gap-y-4 overflow-hidden"
>
<div class="flex-1 space-y-2 overflow-x-hidden overflow-y-auto">
<div class="flex items-center h-[36px]">
Expand Down
32 changes: 30 additions & 2 deletions frontend/src/plugins/cel/logic/build.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { head } from "lodash-es";
import { celServiceClient } from "@/grpcweb";
import { Expr as CELExpr } from "@/types/proto/google/api/expr/v1alpha1/syntax";
import type { ConditionExpr, ConditionGroupExpr, SimpleExpr } from "../types";
import type {
ConditionExpr,
ConditionGroupExpr,
SimpleExpr,
Operator,
} from "../types";
import {
isDirectoryExpr,
isEqualityExpr,
isCollectionExpr,
isConditionExpr,
Expand All @@ -11,6 +17,7 @@ import {
isStringExpr,
ExprType,
isRawStringExpr,
DictionaryOperatorList,
} from "../types";

const seq = {
Expand Down Expand Up @@ -52,6 +59,14 @@ export const buildCELExpr = async (
throw new Error(`unexpected type "${String(expr)}"`);
};
const convertCondition = (condition: ConditionExpr): CELExpr => {
if (isDirectoryExpr(condition)) {
const { args } = condition;
const [factor, key, value] = args;
return wrapCallExpr("_==_", [
wrapDirectoryExpr({ factor, key }),
wrapConstExpr(value),
]);
}
if (isEqualityExpr(condition)) {
const { operator, args } = condition;
const [factor, value] = args;
Expand Down Expand Up @@ -168,8 +183,21 @@ const wrapIdentExpr = (name: string): CELExpr => {
});
};

const wrapDirectoryExpr = ({
factor,
key,
}: {
factor: string;
key: string;
}): CELExpr => {
return wrapCallExpr(DictionaryOperatorList[0], [
wrapIdentExpr(factor),
wrapConstExpr(key),
]);
};

const wrapCallExpr = (
operator: string,
operator: Operator,
args: CELExpr[],
target?: CELExpr
): CELExpr => {
Expand Down
Loading