Appian Community
Site
Search
Sign In/Register
Site
Search
User
DISCUSS
LEARN
SUCCESS
SUPPORT
Documentation
AppMarket
More
Cancel
I'm looking for ...
State
Not Answered
Replies
18 replies
Subscribers
10 subscribers
Views
7143 views
Users
0 members are here
Share
More
Cancel
Related Discussions
Home
»
Discussions
»
Data and Records
Hi all, we are in middle of planning for complete moving data from apps to
natasav
over 9 years ago
Hi all,
we are in middle of planning for complete moving data from apps to tempo interface.
We had a several discussions of what is appian best practise for doing that.
Now wee have several question about records. On the system customer wants to keep all data permanently , so we suggest to do some king of archiving or saving necessary data in SQL database and displaying those data by using tempo report and tempo records in same manner as portal reports and process instance dashboard.
So my question is it possible to set security on record data per instance or entry level in same manner as process instance security? And if it's possible how can we do it?
And on the other hand how can we display a html data by using sail?
BR,Natasa
OriginalPostID-191793
OriginalPostID-191793
Discussion posts and replies are publicly visible
Parents
0
Chris
over 9 years ago
Hi sikhivahans, I would be happy to share my thoughts and methods. The theme of both however will be that the best practice of one group per process instance must be ignored in any environment that will eclipse 100 groups per user. On a lighter note, since neither utilize the 'IN' operator, performance is much better.
1. By using a service-backed record with a!queryEntity(), we can create an even MORE flexible record with complete scalability. This record type allows for completely dynamic security, searching, and performs very well. In our environment with 200,000+ record items, this is accomplished and all (top 100) results returned in under 70 milliseconds with upwards of 20 queryFilter()s applied.
Best practice here is essentially ignored due to scalability concerns, flexibility and performance come standard with queryEntity(). For security, we check a loggedinuser()'s group membership at the beginning of the record expression. If they are a system administrator or a member of any (unlimited) process related administrative groups, we do not apply initial filters on the query entity and return all records, only applying additional filters if facets or search is utilized. This allows us to show records for situations such as, who should see this record?:
- The initiator
- Any approver (multiple steps)
- Any admin (multiple groups)
- System administrators
Theory:
recordExpression:
/* doesUserBelongToGroupNamed is a custom function to retrieve memberships and compare to text name input, bypassing security on doesUserBelongToGroup() */
local!isAdmin: or(doesUserBelongToGroupNamed("xxx"),doesUserBelongToGroupNamed("yyy")),
queryEntity(
nested logicalExpression(s):
AND{
default filter (id > 0), need at least one filter applied
AND{
OR{
if local!isAdmin, re-apply default filter only (filter id > 0)
else apply filters for loggedinuser() = Initiator, Approver, or [any fields]
},
OR{
Search parameters - a!queryFilter()'s, can apply to ANY field!
}
facets, a!queryFilter()s for status, decision, etc
}
}
)
From here we cast the datasubset as the desired CDT type and return.
Also to note, this function is called when rp!id is null. If rp!id is populated (record item clicked for linking), a simple Query Rule will do the trick based on rp!id/unique identifier.
2. I am under the impression that either of the 2 desired enhancements would address the current shortcomings of entity-backed records (except for the additional search capability of service-backed records). Allowing users to select AND vs OR on the default filters would enable you to check for multiple conditions, group memberships or roles within the process which is not available now aside from the restrictive best practice method. Allowing access to rf! data within the 'Value' setting would enable designers to 'trick' the filters. In this scenario, you would create a dummy CDT boolean variable (true) to utilize in the 'Filter' area, the 'Value' could be set to something such as:
or(
doesuserbelongtogroupnamed(loggedinuser(),"Admin Group"),
isusersystemadministrator(loggedinuser()),
loggedinuser() = rf!processInitiator,
loggedinuser() = rf!processApprover1,
loggedinuser() = rf!processApprover2
)
Let me know if I can be of any further assistance, and if anyone would like to see the full code.
Cancel
Vote Up
0
Vote Down
Sign in to reply
Verify Answer
Cancel
Reply
0
Chris
over 9 years ago
Hi sikhivahans, I would be happy to share my thoughts and methods. The theme of both however will be that the best practice of one group per process instance must be ignored in any environment that will eclipse 100 groups per user. On a lighter note, since neither utilize the 'IN' operator, performance is much better.
1. By using a service-backed record with a!queryEntity(), we can create an even MORE flexible record with complete scalability. This record type allows for completely dynamic security, searching, and performs very well. In our environment with 200,000+ record items, this is accomplished and all (top 100) results returned in under 70 milliseconds with upwards of 20 queryFilter()s applied.
Best practice here is essentially ignored due to scalability concerns, flexibility and performance come standard with queryEntity(). For security, we check a loggedinuser()'s group membership at the beginning of the record expression. If they are a system administrator or a member of any (unlimited) process related administrative groups, we do not apply initial filters on the query entity and return all records, only applying additional filters if facets or search is utilized. This allows us to show records for situations such as, who should see this record?:
- The initiator
- Any approver (multiple steps)
- Any admin (multiple groups)
- System administrators
Theory:
recordExpression:
/* doesUserBelongToGroupNamed is a custom function to retrieve memberships and compare to text name input, bypassing security on doesUserBelongToGroup() */
local!isAdmin: or(doesUserBelongToGroupNamed("xxx"),doesUserBelongToGroupNamed("yyy")),
queryEntity(
nested logicalExpression(s):
AND{
default filter (id > 0), need at least one filter applied
AND{
OR{
if local!isAdmin, re-apply default filter only (filter id > 0)
else apply filters for loggedinuser() = Initiator, Approver, or [any fields]
},
OR{
Search parameters - a!queryFilter()'s, can apply to ANY field!
}
facets, a!queryFilter()s for status, decision, etc
}
}
)
From here we cast the datasubset as the desired CDT type and return.
Also to note, this function is called when rp!id is null. If rp!id is populated (record item clicked for linking), a simple Query Rule will do the trick based on rp!id/unique identifier.
2. I am under the impression that either of the 2 desired enhancements would address the current shortcomings of entity-backed records (except for the additional search capability of service-backed records). Allowing users to select AND vs OR on the default filters would enable you to check for multiple conditions, group memberships or roles within the process which is not available now aside from the restrictive best practice method. Allowing access to rf! data within the 'Value' setting would enable designers to 'trick' the filters. In this scenario, you would create a dummy CDT boolean variable (true) to utilize in the 'Filter' area, the 'Value' could be set to something such as:
or(
doesuserbelongtogroupnamed(loggedinuser(),"Admin Group"),
isusersystemadministrator(loggedinuser()),
loggedinuser() = rf!processInitiator,
loggedinuser() = rf!processApprover1,
loggedinuser() = rf!processApprover2
)
Let me know if I can be of any further assistance, and if anyone would like to see the full code.
Cancel
Vote Up
0
Vote Down
Sign in to reply
Verify Answer
Cancel
Children
No Data