How is everyone handling record security, when requirements require dynamic visi

How is everyone handling record security, when requirements require dynamic visibility? For instance, we require records for an application to be visible for all application administrators and also to the process initiator (initiator should only see the record for the process isntance they have initiated). Sometimes, company divisional views are necessary as well - divisional administrators should only see records for their specific division. With nearly 40 applications in production, we do not want to have 3+ records for each application. Just wondering if anyone has experience with similar situations. Thanks!

OriginalPostID-155229

OriginalPostID-155229

  Discussion posts and replies are publicly visible

  • To add onto Bob's comment above, if our environment was setup using this best practice we would currently require 500,000 groups - 11,000 new groups would be added per month. We are very concerned with the performance impacts when having this many groups.
  • mschmitt, would you be able to provide an example of your solution? The issue I'm running into now is passing a record field to an expression rule within the Default Filter 'Value' column (for matching to the 'Field' entry). I see one other post regarding the same issue (which presents with APNX-1-4203-021) - no response to that thread.
  • 0
    Certified Lead Developer
    I can try to whip up something when I get a free moment, at least to confirm whether my original idea is feasible. At the very least I think it might be doable provided a bit of quick querying in the expression (i.e. querying on record rows containing the initiating user).
  • Thanks - I haven't been able to get any context about the record (initiator, etc) into the 'Value' portion of the filter - as it seems nothing from the entity can be used there, which puts a damper on pretty much any querying, filtering or matching.
  • 0
    Certified Lead Developer
    I may have forgotten about that constraint. However I think the solution I have in mind might still work. First, set the record field to "Id" (or whatever the primary key is, for example), and the operator to "In". For the expression (which I recommend eventually migrating off to a separate expression rule), you can still refer to =loggedInUser(). So within the expression, first have it checked whether loggedInUser is a system administrator; if so, do a quick Query Entity and pass back a list of all record IDs. If not, query for record IDs where the stored "initiator" = loggedinuser(), and pass those back. In both cases I believe the record listing will then only show the entries applicable for that user. Also I *think* this query would only be done once upon loading of the list, so it hopefully shouldn't cause much performance impact.
  • Perfect, thanks for all the help. I was able to get this working using the IN operator - returning all records if the loggedinuser() is in one of 3 groups, otherwise returning only record IDs associated with the loggedinuser(). Awesome :)

    fldID

    IN

    if(or(doesuserbelongtogroup(loggedinuser(),getgroupnamed("Developers")),doesuserbelongtogroup(loggedinuser(),getgroupnamed("SIR - Admins")),doesuserbelongtogroup(loggedinuser(),getgroupnamed("SIR - Approval IT"))),rule!QR_SIR_All().fldID,rule!QR_SIR_ByInitiator(user(loggedinuser(),"username")).fldID)
  • Although, as I just remembered and tested - this does not work for end users that do not have permissions to see the groups listed in the expression where we are checking for Admins, which will be just about every case :(. In that situation, the getgroupnamed() function fails. Back to the drawing board..
  • Crisis averted by creating a helper function to base group membership on text name:

    contains(apply(fn!tostring,apply(fn!getgroupnames,getgroupsformemberuser(ri!userIn))),ri!groupName)

    Finalized Default Filter expression:

    if(or(doesuserbelongtogroupnamed(loggedinuser(),"Developers"),doesuserbelongtogroupnamed(loggedinuser(),"SIR - Admins"),doesuserbelongtogroupnamed(loggedinuser(),"SIR - Approval IT")),rule!QR_SIR_All().fldID,rule!QR_SIR_ByInitiator(user(loggedinuser(),"username")).fldID)
  • 0
    Certified Lead Developer
    Out of curiosity, why not just reference a constant that points to the group instead of looking it up by name? That also handles the case of a group's name changing in the future.
  • In this case the end users (for which the expression will be evaluating under), do not have permissions to see group "Developers", for example. So if that group is in a constant (which would be better practice for sure), we would still have to run doesUserBelongToGroup(), which AFAIK requires admin permissions - resulting in an error for end users. Basically the text method is a work-around so the system can decide if an end user is in a group they cannot see under their context.