Hi all, we are in middle of planning for complete moving data from apps to

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

  • @natasav Here goes the answers to the best of my knowledge.

    >> 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?

    Yes, that's possible. But the way we do it entirely depends on the type of the Record.

    Initially you can set the security with respect to the source of the record. For instance, in case of process backed records,it is managed via process instance security, that is, the users who can gain access to a particular record should be the one that should also have access to process(i.e. the individual should be in the security role map in a proper role). Whereas in case of entity backed records, the users should have proper rights against Data Store. Going through the documentation at https://forum.appian.com/suite/help/16.1/Record_Design.html and https://forum.appian.com/suite/help/16.1/Record_Design.html#Record_Security can help you to gain insights over this.

    The security can be additionally imposed in the form of default filters as well (for a Record from any source), if needed. For instance, documentation at https://forum.appian.com/suite/help/16.1/Record_Level_Security_for_Entity_Backed_Records_Best_Practice.html explains how we can also make use of default filter to impose an additional security constraint.

    >> And on the other hand how can we display a html data by using sail?

    I am not sure what you exactly meant by this, but there are few components such as RichText, which can slightly help you rendering html features such as bold text, bullets, italics, underline etc. Though we may not be successful to bring each and every feature from html into SAIL, at-least we can try to replicate for some extent by making use of SAIL components. For instance, a Paging Grid SAIL component can be used to substitute the html table. If you are able to expand on the html content desired by you, the practitioners here could let you know the SAIL components that should be used to substitute or replicate the functionality.

    I tried adding as much as I can, and let's see if any other practitioners are able to provide their valuable inputs.
  • To expand on the record security, we faced the same issues when migrating from portal to tempo. With the goal of removing processes from memory as much as possible, we bypassed (mostly) process-backed records. With many process instances and complex security, we bypassed entity backed records - as best practice of creating one group per process instance is not feasible for us.

    What we do is store user information in the CDT/database (employee ID) for all who act in the process. We then utilize service-backed records to retrieve the data from the DB via a!queryEntity(), filtering for records that employee (loggedinuser()) should have access too. If they are in an admin group, filtering is bypassed and all records are shown, etc. As service-backed records are very customizable, this works out great - with a little extra coding of course (than entity or process-backed).
  • Hi natasav & csteward, we can implement record security in the entity backed records as well based on the user & roles information persisted to the database. As csteward mentioned this requires changes to the process and data persistence.
  • @csteward I am slightly surprised to hear that you have bypassed Entity backed records just because of the groups. As you are storing the fact in database about the user's participation in a process, can't we apply a default filter on the entity backed records which gets only those records that holds relationship with the logged in user?

    To the best of my knowledge, until and unless you have some special considerations, entity backed records are sufficient as long as we store the fact in database about a user's participation.
  • @sikhivahans - Our requirements are typically that multiple users should be able to see a record item, say, process initiator, approver 1, approver 2, approver 3 - where 3 approvers are dynamic based on initiator's user profile, department, etc. While we can apply the filter value for loggedinuser() in entity-backed and show records for the initiator, we are unable to filter based on other fields simultaneously due to the restrictions of not having access to rf! data within the value setting on the filter - or the ability to 'OR' the default filters ('AND' is forced). Generally speaking, any user should be able to access the record and see only items for which they have either initiated or approved. After multiple conversations on forum and with Appian, there was no clean OOTB solution to this aside from process-backed records, so we went with service-backed due to flexibility there. Process-backed records are not feasible for us due to the size of our environment.

    We had a lengthy discussion on forum regarding this topic at the time as well (https://forum.appian.com/suite/tempo/entry/e-155229).
  • Hi all,
    Because now we save all the viewers of one process instance in to process variable is it possible the value of that process variable to be saved as part of CDT in SQL and then used for filtering entry based record? Something similar on the way of using Groups in Appian best practice for Security of the records. Thanks in advance, Natasa
  • While you can most definitely persist viewer/actor data to the DB via CDT (we do the same as a best practice), you will not be able to reference multiple fields for an entity-backed record's Default Filters. You can create a single filter, per say, if you want the loggedinuser() to only see record items for which they are recorded in a value of a single field. However, if you need logic such as "Show record items where loggedinuser() is either the initiator, an approver or a viewer", this is currently not available for entity-backed records without using the group method (one group per process instance). This best practice is suggested for (I believe) situations where a user will be in no more than 100 groups. We have users where this would require 30,000+ groups, so this does not work for us either (I tested 9,000 groups and the user could not even log in).

    Utilizing multiple default filters becomes restrictive as they work under the "AND" operator across all filters, which we cannot configure at this time. Also, the inability to access record fields (rf!) within the Value setting of the filter is a second issue. The latter would allow work arounds such as utilizing a dummy boolean field ("true") as the "Field" setting, operator "=" then the Value could be "=or(loggedinuser()=rf!initiatorField,loggedinuser()=rf!approverField,loggedinuser()=rf!viewerField)". Alternatively, allowing designers to control the primary default filter operator (AND/OR across all filters) would solve these issues.

    Another restriction is that you cannot access child CDT information in default filters. We went through tests with recording all viewer information to a child CDT, then accessing one field (storing viewer information) in the default filter via the "INCLUDES" operator, only to find out child CDT data is inaccessible.

    One earlier recommendation on forum was to use the unique identifier (ID) field in the default filter "Field" setting, "IN" operator, and in the Value field return a list of all ID values where the loggedinuser has participated at any role, which can be done via query rule on a SQL view that organizes this data into one field. Performance results caused us to steer clear of this idea.

    As we have similar security/logic requirements, and too many processes to implement the practice of on group per process instances, we've been forced to utilize service-backed records, which can be fully dynamic with data returned via a!queryEntity(). There is a bit of setup involved, but the tutorial is very helpful: forum.appian.com/.../Service-Backed_Record_Tutorial.html.

    Aside from the initial setup, this method has been very beneficial for us. Security is 100% customizable, and the benefit of controlling which fields the Search references is a nice bonus. Currently Search only utilizes 'Title' data, but with service backed records you can reference any field in your CDT for searching that you would like. The logicalExpressions within queryEntity can be a bit cumbersome, but once you have one record configured - copying and manipulating for future records is much easier.

    An enhancement to expand Default Filters to be expressionable and dynamic is currently being tracked under AN-60876. I have not heard any information as to the progress of this, but we sure would like to see it in the base product.
  • We are moving everything to Tempo and discontinuing Portal. There are hundreds of processes out there in Portal and we don’t want to remove user access one at a time. Instead, we want to remove user access all at the same time on processes in a simple, efficient and secured way. Therefore, how can we accomplish the undermentioned tasks?
    Remove Portal access from all users (they will not be using Portal anymore).
    * Remove user sharing on portal pages, remove user view access from any portal dashboards, processes and process models.
    * We need to verify that older portal processes that grant user view access are closed by the time all users get added to Tempo.
    * Ensure no user access to any document folders that have been generated in Portal.
  • @csteward Hi, as far as I know I have come to know that performance issues arise when following approaches were chosen:

    1. One group per one record - https://forum.appian.com/suite/help/16.1/Record_Level_Security_for_Entity_Backed_Records_Best_Practice.html. This violates the rule '<100 groups per person'. Further you stated that 'I tested 9,000 groups and the user could not even log in'. This is not a good practice definitely and off-course this leads to performance degrade as well.

    2. use the unique identifier (ID) field in the default filter "Field" setting, "IN" operator, and in the Value field return a list of all ID values where the loggedinuser has participated at any role. Off-course, as the list of records in which the user has participated increases, the performance goes down.


    So finally I would like to ask you following questions and it would be grateful if you can answer the following questions:
    1. As we have seen that performance degrade and best practice violation are the disadvantages of above discussed approaches, how did you overcome the disadvantages (in terms of performance and best practice but not in terms of flexibility) by choosing the Service Backed Records?
    2. There are few features which we are actually desiring for, such as access record fields (rf!) within the Value, AND/OR among the default filters. I am interested in knowing from your perspective, if these features can resolve performance issues or best practice violations as discussed above.
  • 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.