Class JpqlAccessSupport
java.lang.Object
io.jmix.aitools.dataload.execution.JpqlAccessSupport
Checks a validated data-load query against the current user's permissions for every entity it reads, and
returns the text to execute. Kept apart from execution so that any way of executing a generated query can
apply the same checks first.
An entity counts as read when the query declares it (root, join, subquery) or when a path passes through it
(o.customer.name reads the customer). The platform checks entity READ and applies row-level conditions
for the root entity only.
Every entity the query reads is also narrowed to the records the AI may see: records of a @ExcludeFromAi
subclass are records of its base entity too, and a query over the base would otherwise return them.
A left join is narrowed in its own on clause, so that a hidden record is joined as an absent one.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprotected static classRow-level query context for a non-root entity.protected static classWhere one entity occurs in a query.protected static final recordAn entity's row-level conditions sorted by how they go into a join'sonclause.protected static final recordEntities a query reads, keyed and ordered by entity name.protected static final recordA reference the query reads by itself or through its primary key.protected static final recordA row-level condition as a constraint added it. -
Field Summary
FieldsModifier and TypeFieldDescriptionprotected AccessManagerprotected JpqlDeclarationSupportprotected JpqlLeftJoinSupportprotected Metadataprotected MetadataToolsprotected QueryParamValuesManagerprotected QueryTransformerFactory -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprotected voidaddPathReachedEntities(QueryParser.QueryPath path, boolean outer, Map<String, JpqlAccessSupport.EntityOccurrence> entities, Map<String, Integer> pathsPerPath, Map<String, Integer> joinsPerPath) applyAccessConstraints(String jpql, Collection<String> parameterNames) Checks the query and its parameters and returns the text to execute, with the row-level conditions of every non-root entity and the exclusion of@ExcludeFromAisubclasses woven in: into theonclause of a left join, into thewhereotherwise.protected StringKeeps the records of@ExcludeFromAisubclasses out of the result: such a subclass is closed to the AI, yet its records are records of every visible ancestor.protected StringapplyLeftJoinConditions(String jpql, JpqlAccessSupport.QueryGraph graph) Narrows the entities joined by aleft joinin the join's ownonclause, so that a hidden record is joined as an absent one and the row stays.protected StringapplyRowLevelConditions(String jpql, JpqlAccessSupport.QueryGraph graph) Returns the text to execute with the row-level conditions of every non-root entity applied.protected voidprotected voidcheckNoJoinClause(MetaClass entity, List<JpqlAccessSupport.RowLevelCondition> conditions) protected voidcheckParameterNames(Collection<String> parameterNames) Refuses query parameters whose values the application supplies itself (current_user_*,session_*,current_locale).protected voidcheckSelectsValues(QueryParser parser) protected JpqlAccessSupport.LeftJoinConditionsclassifyConditions(MetaClass entity, List<JpqlAccessSupport.RowLevelCondition> rowLevelConditions, @Nullable String primaryKey) Sorts the entity's row-level conditions by how they go into a join'sonclause: as they are, or wrapped into a subquery on the entity because they navigate a reference of it, which anonclause cannot do.protected List<JpqlAccessSupport.RowLevelCondition> collectRowLevelConditions(MetaClass entity) Returns the row-level conditions that the registered constraints add for the entity, as they would for a query rooted at it.protected StringexcludedSubtypeCondition(String expression, Set<String> excludedSubtypes) excludedSubtypeNames(MetaClass entity) protected booleanprotected booleanisIdOfReference(MetaProperty[] properties) protected booleanisReadReference(MetaProperty[] properties, QueryParser.QueryPath path, Map<String, Integer> pathsPerPath, Map<String, Integer> joinsPerPath) Tells whether a path is a to-one reference of a variable read by itself (`o.customer is null`, `o.customer = c`), not only as the path a join declares.protected Stringprotected booleannavigatesReference(MetaClass entity, String where) Tells whether a row-level condition reaches beyond the entity's own columns: through a reference to another entity, or into a collection.protected StringLets through a row that refers to no record, since an absent record carries no data.protected JpqlAccessSupport.EntityOccurrenceoccurrenceOf(Map<String, JpqlAccessSupport.EntityOccurrence> entities, String entityName) onConditions(MetaClass entity, JpqlAccessSupport.LeftJoinConditions conditions, Set<String> excludedSubtypes, String variable, JpqlLeftJoinSupport.Rewrite rewrite) Returns the conditions narrowing one variable of the entity in its join'sonclause.protected JpqlAccessSupport.QueryGraphreadGraph(String jpql, QueryParser parser) Collects every entity the query reads, with the expressions that stand for it in the outer query and whether it also occurs where a condition added to the outer query cannot reach it.protected StringreferenceExistsCondition(String referencePath, MetaClass owner, JpqlLeftJoinSupport.Rewrite rewrite) Returns a condition keeping only the rows whose owner refers to a record through the reference, as the inner join of a selected<ref>.<id>path does.targetsOf(MetaClass entity, JpqlAccessSupport.EntityOccurrence occurrence, List<JpqlAccessSupport.RowLevelCondition> conditions, JpqlAccessSupport.QueryGraph graph) Returns the expressions to apply the entity's row-level conditions to: every expression standing for it in the outer query except the root alias, which the platform narrows itself.
-
Field Details
-
accessManager
-
queryTransformerFactory
-
metadata
-
metadataTools
-
declarationSupport
-
leftJoinSupport
-
queryParamValuesManager
-
-
Constructor Details
-
JpqlAccessSupport
public JpqlAccessSupport()
-
-
Method Details
-
applyAccessConstraints
Checks the query and its parameters and returns the text to execute, with the row-level conditions of every non-root entity and the exclusion of@ExcludeFromAisubclasses woven in: into theonclause of a left join, into thewhereotherwise. A text the JPQL parser cannot read is returned unchanged, since executing it fails on the same parser.- Parameters:
jpql- validated query textparameterNames- names of the parameters passed with the query- Returns:
- the text to execute
- Throws:
AccessDeniedException- if the current user may not read an entity the query readsJpqlAccessConstraintException- if the query cannot be narrowed as the current user's permissions require
-
checkParameterNames
Refuses query parameters whose values the application supplies itself (current_user_*,session_*,current_locale). Row-level conditions refer to them, and a value passed with the query would take the place of the application's.- Parameters:
parameterNames- names of the parameters passed with the query- Throws:
JpqlAccessConstraintException- if one of them is supplied by the application
-
readGraph
Collects every entity the query reads, with the expressions that stand for it in the outer query and whether it also occurs where a condition added to the outer query cannot reach it.- Parameters:
jpql- query textparser- parser of the query- Returns:
- the query graph
-
addPathReachedEntities
protected void addPathReachedEntities(QueryParser.QueryPath path, boolean outer, Map<String, JpqlAccessSupport.EntityOccurrence> entities, Map<String, Integer> pathsPerPath, Map<String, Integer> joinsPerPath) -
isIdOfReference
-
isReadReference
protected boolean isReadReference(MetaProperty[] properties, QueryParser.QueryPath path, Map<String, Integer> pathsPerPath, Map<String, Integer> joinsPerPath) Tells whether a path is a to-one reference of a variable read by itself (`o.customer is null`, `o.customer = c`), not only as the path a join declares.- Parameters:
properties- the path's propertiespath- the pathpathsPerPath- number of times each path occurs in the query, keyed in lower casejoinsPerPath- number of joins declared over each path, keyed in lower case- Returns:
- whether the path reads the reference
-
lowerCase
-
checkEntityReadPermitted
-
checkSelectsValues
-
applyLeftJoinConditions
Narrows the entities joined by aleft joinin the join's ownonclause, so that a hidden record is joined as an absent one and the row stays. Covers row-level conditions and excluded subclasses alike.- Parameters:
jpql- query textgraph- graph of the query- Returns:
- the text with the conditions in the
onclauses, unchanged when there is nothing to apply - Throws:
JpqlAccessConstraintException- if a row-level condition to apply carries a join; if a navigating condition has to be wrapped into a subquery and the entity has no primary key; or if a reference read through a<ref>or<ref>.<id>path occurs in theonclause of the left join declaring its variableIllegalStateException- if the query text cannot be rewritten as its graph requires
-
referenceExistsCondition
protected String referenceExistsCondition(String referencePath, MetaClass owner, JpqlLeftJoinSupport.Rewrite rewrite) Returns a condition keeping only the rows whose owner refers to a record through the reference, as the inner join of a selected<ref>.<id>path does. It reads the owner through a subquery: a condition on<owner>.<ref>itself would be merged with the left join added for the path and drop the rows whose record is hidden.- Parameters:
referencePath- path from the owner variable to the reference, such aso.customerowner- entity the owner variable ranges overrewrite- rewrite of the query, used for a fresh variable name- Returns:
- the condition
- Throws:
IllegalStateException- if the owner entity has no primary key
-
classifyConditions
protected JpqlAccessSupport.LeftJoinConditions classifyConditions(MetaClass entity, List<JpqlAccessSupport.RowLevelCondition> rowLevelConditions, @Nullable String primaryKey) Sorts the entity's row-level conditions by how they go into a join'sonclause: as they are, or wrapped into a subquery on the entity because they navigate a reference of it, which anonclause cannot do. Decided once per condition, not per variable it is applied to.- Parameters:
entity- entity the conditions belong torowLevelConditions- the entity's row-level conditionsprimaryKey- name of the entity's primary key, ornullif it has none- Returns:
- the conditions, with the
{E}placeholder still in place - Throws:
JpqlAccessConstraintException- if a condition navigates and the entity has no primary key to wrap it into a subquery by
-
onConditions
protected List<String> onConditions(MetaClass entity, JpqlAccessSupport.LeftJoinConditions conditions, Set<String> excludedSubtypes, String variable, JpqlLeftJoinSupport.Rewrite rewrite) Returns the conditions narrowing one variable of the entity in its join'sonclause.- Parameters:
entity- entity the variable ranges overconditions- the entity's row-level conditions, sorted byclassifyConditions(io.jmix.core.metamodel.model.MetaClass, java.util.List<io.jmix.aitools.dataload.execution.JpqlAccessSupport.RowLevelCondition>, java.lang.String)excludedSubtypes- names of the entity's@ExcludeFromAidescendantsvariable- variable to narrowrewrite- rewrite of the query, used for fresh variable names- Returns:
- the conditions, with the variable in place of
{E}
-
applyRowLevelConditions
Returns the text to execute with the row-level conditions of every non-root entity applied. These are the conditions the platform would apply to a query rooted at that entity.- Parameters:
jpql- validated query textgraph- graph of the query- Returns:
- the text to execute, unchanged when there is nothing to apply
- Throws:
JpqlAccessConstraintException- if an entity with conditions occurs where they cannot be applied, or a condition of a non-root entity carries a join
-
applyExcludedSubtypeConditions
Keeps the records of@ExcludeFromAisubclasses out of the result: such a subclass is closed to the AI, yet its records are records of every visible ancestor. Unlike row-level conditions, this applies to the root alias too, since the platform knows nothing of the annotation.- Parameters:
jpql- query textgraph- graph of the query- Returns:
- the text to execute, unchanged when no entity the query reads has an excluded subclass
- Throws:
JpqlAccessConstraintException- if an entity with an excluded subclass occurs where a condition added to the outer query cannot reach it
-
excludedSubtypeCondition
-
excludedSubtypeNames
-
nullSafe
Lets through a row that refers to no record, since an absent record carries no data. Absence is checked on the owner's foreign key only, because a check on the joined variable would let anonclause probe a hidden record.- Parameters:
nullCheck- path whoseis nulltells that no record is referred to, ornullwhen absence cannot be told that way (a collection, the inverse side, an entity join)condition- condition with the target in place of{E}- Returns:
- the condition to add to the
where
-
targetsOf
protected Map<String,@Nullable String> targetsOf(MetaClass entity, JpqlAccessSupport.EntityOccurrence occurrence, List<JpqlAccessSupport.RowLevelCondition> conditions, JpqlAccessSupport.QueryGraph graph) Returns the expressions to apply the entity's row-level conditions to: every expression standing for it in the outer query except the root alias, which the platform narrows itself.- Parameters:
entity- entity the conditions belong tooccurrence- where the entity occurs in the queryconditions- the entity's row-level conditions, not emptygraph- graph of the query- Returns:
- target expressions, each with its null check or
null; empty when the platform applies them all - Throws:
JpqlAccessConstraintException- if the entity occurs where the conditions cannot be applied, or a condition to apply carries a join
-
checkNoJoinClause
protected void checkNoJoinClause(MetaClass entity, List<JpqlAccessSupport.RowLevelCondition> conditions) -
collectRowLevelConditions
Returns the row-level conditions that the registered constraints add for the entity, as they would for a query rooted at it.- Parameters:
entity- entity to collect the conditions of- Returns:
- the conditions with the
{E}placeholder still in place; empty if none
-
isBlank
-
occurrenceOf
protected JpqlAccessSupport.EntityOccurrence occurrenceOf(Map<String, JpqlAccessSupport.EntityOccurrence> entities, String entityName)
-