rusackas commented on code in PR #33924:
URL: https://github.com/apache/superset/pull/33924#discussion_r3599827086
##########
docs/admin_docs/configuration/sql-templating.mdx:
##########
@@ -372,6 +372,41 @@ Here's a concrete example:
WHERE country_code = 'US'
```
+**Guest User Attributes**
+
+The `{{ get_guest_user_attribute('attribute_name') }}` macro returns a
specific attribute value from the guest user context.
+This is useful when working with embedded Superset where guest tokens can
contain custom attributes that need to be
+accessed in SQL queries.
+
+This macro only works when the current user is a guest user (authenticated via
guest token). If the current user is
+not a guest user, or if the specified attribute doesn't exist, the macro will
return `None` or the provided default value.
+
+If you have caching enabled in your Superset configuration, then by default
the attribute value will be used
+by Superset when calculating the cache key. A cache key is a unique identifier
that determines if there's a
+cache hit in the future and Superset can retrieve cached data.
+
+You can disable the inclusion of the attribute value in the calculation of the
+cache key by adding the following parameter to your Jinja code:
+
+```
+{{ get_guest_user_attribute('department', add_to_cache_keys=False) }}
+```
+
+You can also provide a default value if the attribute is not found:
+
+```
+{{ get_guest_user_attribute('region', default='US') }}
+```
+
+Here's a concrete example of using guest user attributes in a query:
+
+```sql
+SELECT *
+FROM sales_data
+WHERE region = '{{ get_guest_user_attribute("user_region", default="global")
}}'
+ AND department = '{{ get_guest_user_attribute("department") }}'
Review Comment:
Fair point. Added a Security Warning here in 5d43b59, mirroring the existing
`url_param` guidance: guest token attributes are not escaped by Superset, so
validate/allowlist them in the embedding backend before interpolating into SQL.
Resolving.
##########
tests/unit_tests/jinja_context_test.py:
##########
@@ -1974,10 +1975,944 @@ def
test_undefined_template_variable_not_function(mocker: MockerFixture) -> None
("SELECT {{ current_user_email() }}", True),
("SELECT {{ current_user_roles() }}", True),
("SELECT {{ current_user_rls_rules() }}", True),
+ ("SELECT {{ get_guest_user_attribute('department') }}", True),
("SELECT 'cache_key_wrapper(abc)' AS false_positive", False),
("SELECT 1", False),
("SELECT '{{ 1 + 1 }}'", False),
],
)
def test_extra_cache_regex(sql: str, expected: bool) -> None:
assert bool(ExtraCache.regex.search(sql)) is expected
+
+
+def test_get_guest_user_attribute_no_request_context(mocker: MockerFixture) ->
None:
+ """
+ Test that get_guest_user_attribute returns default when there is no guest
user
+ (e.g. outside of a request context).
+ """
+ mocker.patch("superset.security_manager.is_guest_user", return_value=False)
+
+ cache = ExtraCache()
+ result = cache.get_guest_user_attribute("department", "default_dept")
+ assert result == "default_dept"
Review Comment:
Good eye. This turned out to be three byte-identical copies of the same
guard test (`_no_request_context` / `_no_user` / `_not_guest_user`), all
mocking `is_guest_user=False` and asserting the same thing. Dropped the two
redundant/mislabeled ones in 5d43b59 and kept
`test_get_guest_user_attribute_not_guest_user`, which is the accurately-named
one. Resolving.
##########
superset/jinja_context.py:
##########
@@ -305,6 +311,57 @@ def url_param(
self.cache_key_wrapper(result)
return result
+ def get_guest_user_attribute(
+ self,
+ attribute_name: str,
+ default: JsonValue = None,
+ add_to_cache_keys: bool = True,
+ ) -> JsonValue:
+ """
+ Get a specific user attribute from guest user.
+
+ This function retrieves attributes from the guest user token and
supports
+ all JSON-native types (string, number, boolean, array, object, null).
+
+ Args:
+ attribute_name: Name of the attribute to retrieve
+ default: Default value if attribute not found (can be any
JSON-native type)
+ add_to_cache_keys: Whether the value should be included in the
cache key
+
+ Returns:
+ The attribute value from the guest user token, or the default
value.
+ Can be any JSON-native type: string, number, boolean, array,
object, or
+ null.
+
+ Examples:
+ {{ get_guest_user_attribute('department') }} # Returns:
"Engineering"
+ {{ get_guest_user_attribute('is_admin') }} # Returns: True
+ {{ get_guest_user_attribute('permissions') }} # Returns: ["read",
"write"]
+ {{ get_guest_user_attribute('config') }} # Returns: {"theme":
"dark"}
+ {{ get_guest_user_attribute('missing', 'default') }} # Returns:
"default"
+ """
+
+ # The macro only applies to guest users (embedded). is_guest_user()
+ # handles the feature-flag and request-context checks internally.
+ if not security_manager.is_guest_user():
+ return default
+
+ token = g.user.guest_token
+ user_attributes = token.get("user", {}).get("attributes") or {}
+
+ # Only add to the cache key if the attribute exists in the guest token
+ if attribute_name not in user_attributes:
+ return default
+
+ result = user_attributes[attribute_name]
+ if add_to_cache_keys and result is not None:
Review Comment:
This mirrors the existing `current_user_id` / `current_username` /
`current_user_roles` macros, which also only add to the cache key when the
value is present (they skip `cache_key_wrapper` when the user/value is absent).
It is a pre-existing property of the whole macro family rather than something
this PR introduces, so changing just this one macro would make it diverge from
its siblings. Out of scope here, so resolving.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]