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

Parents
  • 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.
Reply
  • 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.
Children
No Data