Skip to main content
Announcements
Qlik Connect 2024! Seize endless possibilities! LEARN MORE
cancel
Showing results for 
Search instead for 
Did you mean: 
mbespartochnyy
Creator III
Creator III

What is the difference between resource.stream.HasPrivilege and resource.app.stream.HasPrivilege?

I'm working on default security rule named Stream. Here's the link to Qlik's support documentation to the exact rule that I'm referring to:

https://help.qlik.com/en-US/sense/June2019/Subsystems/ManagementConsole/Content/Sense_QMC/preinstall...

Condition of that rule references two seemingly same things but they are written differently. The fact that they are written differently makes me think that there is a difference between them.

Does anyone know if there is a difference between resource.stream.HasPrivilege("Read") and resource.app.stream.HasPrivilege("Read")? And if there is a difference, does anyone know what that difference is?

5 Replies
Nicole-Smith

I believe resource.stream.HasPrivilege("Read") is for the stream itself and resource.app.stream.HasPrivilege("Read") are for items published within the stream.  

Because you could technically have a security rule that says that Person has access to Stream which contains App1 and App2 but they only can access App1 and not App2.

mbespartochnyy
Creator III
Creator III
Author

I see. So a "stream" is an attribute that is available at at least two different levels - at a stream level and at an app level. In other words, stream is a resource in and of itself, but each published app is also assigned to a stream, enabling evaluation of read rights at both stream and combination of app and stream levels.

To go along with your example, in a case when user has access to a stream and only to App1, resource.stream.HasPrivilege("Read") will evaluate to true for stream that a user has access to. resource.app.stream.HasPrivilege("Read") will also evaluate to true for a combination of stream and App1, but it will evaluate to false for a combination of stream and App2 (because there's some other rule in place that allows user to have access only to App1).

Is that right?

Levi_Turner
Employee
Employee

You've stumbled on the best rule to illustrate how things work 😀.

tl;dr there is a difference. The difference is from the type of thing the rule is scoping to and the syntax is from the hierarchy of the thing (objects are in apps which are in streams).

First, let's do an introduction to rules. All rules have 4 components:

  1. A filter
  2. An action or set of actions
  3. A condition or set of conditions
  4. A context

A filter defines what type of thing the rule will be against. For the Stream rule the filter is App*. This is a great working example since it shows the power of wildcards. App* scopes to two types of things App_* (all apps) and App.Object_* (all objects like stories, sheets, bookmarks, load scripts, etc).

Actions are straight-forward since they scope to the types of activities the users who fulfill the conditions can do against the types of things defined in the filter.

Conditions are used to qualify both the types of things (filter) as well as the types of users.

Context is the place where the rule is parsed (Hub, QMC, Both).

Back to your question, the Stream rule is as follows:

Filter: App*
Actions: Read:
Conditions: (resource.resourcetype = "App" and resource.stream.HasPrivilege("read")) or ((resource.resourcetype = "App.Object" and resource.published ="true" and resource.objectType != "app_appscript" and resource.objectType != "loadmodel") and resource.app.stream.HasPrivilege("read"))
Context: Both

 

The first clause is this:

(resource.resourcetype = "App" and resource.stream.HasPrivilege("read"))

The way of semantically describing this rule is:

  •  For all resources (App*) which are Apps, grant read access to the App is the user has read rights on the Stream.

Apps are members of streams and resource is an App based on the resource.resourcetype qualification.

The second clause is this:

((resource.resourcetype = "App.Object" and resource.published ="true" and resource.objectType != "app_appscript" and resource.objectType != "loadmodel") and resource.app.stream.HasPrivilege("read"))

 

The way of semantically describing this rule is:

  • For all resources (App*) which are App.Objects, grant read access to all published and approved objects which are in apps which are in streams the user has read rights on.

Since App.Objects are members of Apps which are members of streams, you'd use resource.app.stream to transverse that hierarchy. resource here is an App.Object so you need to declare the next part (app) to have access to the stream relationship.

For another way of consuming this information, please refer to my session on rules: https://youtu.be/h5nBdt969XI?t=479

Great, (hopefully) I've made this clear. The next question is what kind of hierarchies exist? Well, most are obvious like streams > apps > objects. For more obscure ones, I find it easiest to reference the GET /qrs/about/api/relations endpoint in the Repository Service (https://help.qlik.com/en-US/sense-developer/September2020/APIs/RepositoryServiceAPI/index.html?page=...). The response is:

[
  "App.owner > User",
  "App.stream > Stream",
  "App.tags > Tag",
  "App.Content.app > App",
  "App.Content.references > StaticContentReference",
  "App.Content.whiteList > FileExtensionWhiteList",
  "App.DataSegment.app > App",
  "App.DataSegment.file > FileReference",
  "App.DataSegment.owner > User",
  "App.Internal.app > App",
  "App.Internal.file > FileReference",
  "App.Object.app > App",
  "App.Object.file > FileReference",
  "App.Object.owner > User",
  "App.Object.tags > Tag",
  "AppSeedInfo.app > App",
  "AppStatus.app > App",
  "CompositeEvent.externalProgramTask > ExternalProgramTask",
  "CompositeEvent.operational > CompositeEventOperational",
  "CompositeEvent.reloadTask > ReloadTask",
  "CompositeEvent.userSyncTask > UserSyncTask",
  "CompositeEvent.Rule.externalProgramTask > ExternalProgramTask",
  "CompositeEvent.Rule.operational > CompositeEventRuleOperational",
  "CompositeEvent.Rule.reloadTask > ReloadTask",
  "CompositeEvent.Rule.userSyncTask > UserSyncTask",
  "ContentLibrary.owner > User",
  "ContentLibrary.references > StaticContentReference",
  "ContentLibrary.tags > Tag",
  "ContentLibrary.whiteList > FileExtensionWhiteList",
  "CustomPropertyValue.definition > CustomPropertyDefinition",
  "DataConnection.owner > User",
  "DataConnection.tags > Tag",
  "EngineService.serverNodeConfiguration > ServerNodeConfiguration",
  "EngineService.tags > Tag",
  "ExecutionResult.details > ExecutionResult.Detail",
  "ExecutionSession.app > App",
  "ExecutionSession.executingNode > SchedulerService",
  "ExecutionSession.executionResult > ExecutionResult",
  "ExecutionSession.externalProgramTask > ExternalProgramTask",
  "ExecutionSession.reloadTask > ReloadTask",
  "ExecutionSession.userSyncTask > UserSyncTask",
  "Extension.owner > User",
  "Extension.references > StaticContentReference",
  "Extension.tags > Tag",
  "Extension.whiteList > FileExtensionWhiteList",
  "ExternalProgramTask.operational > ExternalProgramTaskOperational",
  "ExternalProgramTask.qlikUser > User",
  "ExternalProgramTask.tags > Tag",
  "ExternalProgramTaskOperational.lastExecutionResult > ExecutionResult",
  "FileExtension.mimeType > MimeType",
  "FileExtensionWhiteList.fileExtensions > FileExtension",
  "License.AnalyzerAccessType.user > User",
  "License.AnalyzerAccessUsage.analyzerAccessType > License.AnalyzerAccessType",
  "License.AnalyzerTimeAccessUsage.analyzerTimeAccessType > License.AnalyzerTimeAccessType",
  "License.AnalyzerTimeAccessUsage.user > User",
  "License.LoginAccessUsage.loginAccessType > License.LoginAccessType",
  "License.LoginAccessUsage.user > User",
  "License.ProfessionalAccessType.user > User",
  "License.ProfessionalAccessUsage.professionalAccessType > License.ProfessionalAccessType",
  "License.UserAccessType.user > User",
  "License.UserAccessUsage.userAccessType > License.UserAccessType",
  "OdagEngineGroup.owner > User",
  "OdagLink.modelGroups > OdagModelGroup",
  "OdagLink.owner > User",
  "OdagLink.templateApp > App",
  "OdagLinkUsage.link > OdagLink",
  "OdagLinkUsage.selectionApp > App",
  "OdagModelGroup.owner > User",
  "OdagRequest.engineGroup > OdagEngineGroup",
  "OdagRequest.generatedApp > App",
  "OdagRequest.link > OdagLink",
  "OdagRequest.owner > User",
  "OdagService.Settings.anonymousProxyUser > User",
  "PrintingService.serverNodeConfiguration > ServerNodeConfiguration",
  "PrintingService.tags > Tag",
  "ProxyService.serverNodeConfiguration > ServerNodeConfiguration",
  "ProxyService.tags > Tag",
  "ProxyServiceCertificate.proxyService > ProxyService",
  "ProxyService.Settings.virtualProxies > VirtualProxyConfig",
  "ReloadTask.app > App",
  "ReloadTask.operational > ReloadTaskOperational",
  "ReloadTask.tags > Tag",
  "ReloadTaskOperational.lastExecutionResult > ExecutionResult",
  "RepositoryService.serverNodeConfiguration > ServerNodeConfiguration",
  "RepositoryService.tags > Tag",
  "SchedulerService.serverNodeConfiguration > ServerNodeConfiguration",
  "SchedulerService.tags > Tag",
  "SchemaEvent.externalProgramTask > ExternalProgramTask",
  "SchemaEvent.operational > SchemaEventOperational",
  "SchemaEvent.reloadTask > ReloadTask",
  "SchemaEvent.userSyncTask > UserSyncTask",
  "ServerNodeConfiguration.roles > ServerNodeRole",
  "ServerNodeConfiguration.serviceCluster > ServiceCluster",
  "ServerNodeConfiguration.tags > Tag",
  "ServerNodeHeartbeat.serverNodeConfiguration > ServerNodeConfiguration",
  "ServiceStatus.serverNodeConfiguration > ServerNodeConfiguration",
  "SharedContent.owner > User",
  "SharedContent.references > StaticContentReference",
  "SharedContent.tags > Tag",
  "SharedContent.whiteList > FileExtensionWhiteList",
  "StaticContentReference.files > FileReference",
  "Stream.owner > User",
  "Stream.tags > Tag",
  "SyncSession.serverNodeConfiguration > ServerNodeConfiguration",
  "SystemNotification.reloadTasks > ReloadTask",
  "SystemNotification.targetUsers > User",
  "SystemRule.tags > Tag",
  "TempContent.owner > User",
  "TermsAcceptance.user > User",
  "User.tags > Tag",
  "UserDirectory.tags > Tag",
  "UserSyncTask.operational > UserSyncTaskOperational",
  "UserSyncTask.tags > Tag",
  "UserSyncTask.userDirectory > UserDirectory",
  "UserSyncTaskOperational.lastExecutionResult > ExecutionResult",
  "VirtualProxyConfig.loadBalancingServerNodes > ServerNodeConfiguration",
  "VirtualProxyConfig.tags > Tag",
  "WebExtensionLibrary.owner > User",
  "WebExtensionLibrary.tags > Tag",
  "Widget.extensionType > WebExtensionType",
  "Widget.library > WebExtensionLibrary",
  "Widget.owner > User",
  "Widget.tags > Tag"
]

 

From there we can see the hierarchies which are available for you to traverse if needed.

Hope that is as clear as it can be.

mbespartochnyy
Creator III
Creator III
Author

This sheds so much light on it. Thanks for taking time to reply and provide the details, @Levi_Turner ! I read through your reply, watched your presentation and had to take a bit of time to mull it over, but I think I understood it now.

Slight Detour For Context

It looks like conditions, specifically in the case of security rule named Stream, are operating more like extensions to the resource filter. In other words, we can't really select all but two types of app objects in apps that are in streams that a user has read access to. In resource filter, we do have flexibility to select many different resources, but we do not have flexibility to make selections of things like app objects that are not script or data model. In resource filter we can select all app objects or a single app object, but if we want to make selections of all except for some, we have to combine resource filter with conditions to get us down to specific list of resources that we're after.

Back to the Topic

First clause
If I understood purpose of conditions in Stream rule correctly, implication is that when App* is provided as resource filter, what returned is an object ("object" in JavaScript terms) with bunch of properties. One of the properties is resourcetype. This property holds values that separates app from app objects and app data segments and app contents and all other things that are associated with an app. In the 

 

resource.resourcetype = "App"

 

piece of condition then, we use say, "reduce everything that is returned by App* resource filter down to just the 'App'." Moreover, having resource.resourcetype = "App" specified, the definition of "resource" in 

 

resource.stream.HasPrivilege("read")

 

piece was changed to "App". So in other word the resource.stream.HasPrivilege("Read") essentially now is App.stream.HasPrivilege("Read"). Is that right?

Second Clause
In the second clause, the

 

resource.resourcetype = "App.Object"

 

piece filters out everything returned by App* filter down to app objects and it also assign definition of resource to be "app object". Then with "resource" redefined, we're tapping into properties of app object like objectType and published. And when it comes to resource.app.stream.HasPrivilege("Read"), because resource has been redefined to mean app object, which is not directly associated with streams, we use app property and it's association with stream to evaluate whether a user has access to a stream in which an app is published that holds app objects that we're granting access to.

Based on all I've read and videos, including yours, that I've watched that seems to be what's going on in those condition clauses and what the difference is between resource.stream.HasPrivilege and resource.app.stream.HasPrivilege. Is that correct or did I misunderstood it in any way?

mbespartochnyy
Creator III
Creator III
Author

Hi @Levi_Turner , I wanted to follow up. If you get a chance, would you please let me know if what I wrote in my previous reply is a correct understanding?