Data masking
With data masking, you can replace sensitive member values with masked
alternatives instead of hiding them entirely. Users who don’t have full access
to a member will see a transformed value (e.g., ***, -1, NULL) rather
than receiving an error.
Data masking extends member-level security. While member-level security controls whether a member is accessible, data masking controls what value is returned when a member is not fully accessible.
Defining masks
You can define a mask parameter on any dimension or
measure. The mask specifies the replacement value used
when the member is masked by an access policy.
Masks can be:
- Static values: numbers, booleans, or strings
- SQL expressions: dynamic masks using
sqlthat can reference cube columns
cubes:
- name: orders
# ...
dimensions:
- name: secret_code
sql: secret_code
type: string
mask:
sql: "CONCAT('***', RIGHT({CUBE}.secret_code, 3))"
- name: revenue
sql: revenue
type: number
mask: -1
- name: is_vip
sql: is_vip
type: boolean
mask: FALSE
measures:
- name: count
type: count
mask: 0cube(`orders`, {
// ...
dimensions: {
secret_code: {
sql: `secret_code`,
type: `string`,
mask: {
sql: (CUBE) => `CONCAT('***', RIGHT(${CUBE}.secret_code, 3))`
}
},
revenue: {
sql: `revenue`,
type: `number`,
mask: -1
},
is_vip: {
sql: `is_vip`,
type: `boolean`,
mask: false
}
},
measures: {
count: {
type: `count`,
mask: 0
}
}
})If no mask is defined on a member, the default mask value is NULL. You can
customize the default mask for each data type using environment variables:
CUBEJS_ACCESS_POLICY_MASK_STRING— default mask forstringmembersCUBEJS_ACCESS_POLICY_MASK_NUMBER— default mask fornumbermembersCUBEJS_ACCESS_POLICY_MASK_BOOLEAN— default mask forbooleanmembersCUBEJS_ACCESS_POLICY_MASK_TIME— default mask fortimemembers
Configuring masking in access policies
Use the member_masking parameter in an access policy to specify
which members should return masked values. The member_masking parameter works
alongside member_level — it requires member_level to be defined in the same policy.
Members listed in member_level get full access (real values). Members not in
member_level but included in member_masking get masked values. Members in
neither are denied entirely.
cubes:
- name: orders
# ...
dimensions:
- name: status
sql: status
type: string
- name: secret_code
sql: secret_code
type: string
mask:
sql: "CONCAT('***', RIGHT({CUBE}.secret_code, 3))"
- name: revenue
sql: revenue
type: number
mask: -1
measures:
- name: count
type: count
mask: 0
access_policy:
- group: manager
member_level:
includes:
- status
- count
member_masking:
includes: "*"cube(`orders`, {
// ...
dimensions: {
status: {
sql: `status`,
type: `string`
},
secret_code: {
sql: `secret_code`,
type: `string`,
mask: {
sql: (CUBE) => `CONCAT('***', RIGHT(${CUBE}.secret_code, 3))`
}
},
revenue: {
sql: `revenue`,
type: `number`,
mask: -1
}
},
measures: {
count: {
type: `count`,
mask: 0
}
},
access_policy: [
{
group: `manager`,
member_level: {
includes: [
`status`,
`count`
]
},
member_masking: {
includes: `*`
}
}
]
})With this policy, users in the manager group will see:
| Member | Value |
|---|---|
status | Real value (full access via member_level) |
count | Real value (full access via member_level) |
secret_code | Masked via SQL expression: ***xyz |
revenue | Masked: -1 |
Policy evaluation with masking
Masking follows the same pattern as row-level security: it is applied at both cube and view levels. When a cube is accessed through a view, both the cube’s and the view’s masking policies are evaluated.
If any applicable policy grants full access to a member (via member_level),
the member is not masked. A member is only masked when no applicable policy
grants it full access and at least one policy includes it in member_masking.
Common patterns
Mask all non-public members
Use member_masking: { includes: "*" } to mask all members that aren’t
explicitly included in member_level:
access_policy:
- group: "*"
member_level:
includes:
- status
- created_at
member_masking:
includes: "*"access_policy: [
{
group: `*`,
member_level: {
includes: [`status`, `created_at`]
},
member_masking: {
includes: `*`
}
}
]Full access for privileged groups, masking for others
Define one policy that masks everything for the default group and another that grants full access to a privileged group:
access_policy:
- group: "*"
member_level:
includes: []
member_masking:
includes: "*"
- group: admin
member_level:
includes: "*"
row_level:
allow_all: trueaccess_policy: [
{
group: `*`,
member_level: {
includes: []
},
member_masking: {
includes: `*`
}
},
{
group: `admin`,
member_level: {
includes: `*`
},
row_level: {
allow_all: true
}
}
]Was this page useful?