Get authorization group for Shiny app user on RStudio Connect

I have an application where I would like the data filtered based upon group membership. Suppose that authentication takes place with double-bind LDAP/AD.

  1. Does the Shiny session object expose this in any way? I see there is a groups element, but I can't find what that actually represents.
  2. Is there an RStudio Connect API method I can use from the Shiny app to get this information?

My end-goal is something along the lines of

data %>% filter(Access %in% authorization_groups)
# Returns rows where data column "Access"  matches a value in authorization_groups

This is a fantastic question! session$groups should provide you the value of the "viewer's" group memberships (as a vector). This is dependent on Group information / configuration being done on the RStudio Connect server.

I think session$groups is definitely going to give you the easiest path here.

I typically use this app to explore what the session object looks like:

As you can see in my case, it shows the groups that I am a member of for easy access:

1 Like

That Shiny app is very helpful -- much better than what I had come up with for session introspection!

This is dependent on Group information / configuration being done on the RStudio Connect server.

I take this to mean that LDAP groups need to be copied into RSConnect. Would you recommend doing something like to do so?

Is my hypothetical scenario below correct and reasonable?

Suppose that I port Group1, consisting of User1 and User2, from LDAP to RSConnect. I then add User3 to Group1 in LDAP. User3 would be able to authenticate for the application because in LDAP they are in Group1, but session$groups would not return "Group1". To remedy this, I would want to schedule something (RMarkdown on RSConnect, cron job, ...) which would update the groups every X. It might function be removing all RSConnect groups and recreating them using the link referenced above.

So the pattern you discuss would be necessary if you were using an authentication provider that does not have support for "querying" the identity store (i.e. SAML, PAM, Proxied Auth, etc.).

However, things are even easier for LDAP / AD authentication! You can just configure Connect to talk appropriately to your LDAP / AD server, and Connect will pick up the groups, pick up the new memberships for you, etc. So you only have a bit of Connect configuration to do, and then you should be good to go!

The process is discussed generally here:

The full suite of options that you can configure are here (search for "Group" to see the relevant settings I am discussing):

If you have any trouble getting this configuration working, feel free to open a support ticket! We would definitely love for you to be taking advantage of group memberships on your Connect server!

1 Like

Thanks Cole! I was aware of those resources but wanted to make sure I was not misinterpreting anything. I'll follow-up with a support ticket if necessary.

1 Like

Thanks for submitting the support request, and I am so sorry for leading you astray here! I completely missed that this is a feature that we do not yet support for LDAP authentication :disappointed:

I suspect the group enumeration from the external authentication provider is the challenge here.

That's okay. I've find a solution which does not rely on session$groups which better fits my needs. However, I do see this as useful in the future, so hopefully it can be added one day.

Good to hear! We definitely do intend to add this feature!

Do you mind articulating a bit about your current solution? If it's possible to share, it may be useful to another user in this situation!

Gladly. If anyone finds anything below unclear, I would be happy to elaborate.

My overall goal is to offer different capabilities based upon a user's group membership. In this case, it limits which observations they can see.

At first, I planned to use session$groups. The app would authenticate through RStudio Connect using group "AppUsers". Membership in other groups then determined the scope of data access. A simple lookup table lookup mapped between the group and corresponding value for data filtering. A rough sketch in R is below.

# initial solution sketch

# global.R
# Each data set is pulled from RSC an rmd report which
# refreshes hourly. They are wrapped in reactivePoll which checks for 
# updates every 15 minutes, so the user privileges will never be more
# than 75 minutes out of date. Looking forward to pins package!
lookup <- data.frame(ad_group = c("Dept1", "Dept2"), ind = c(1, 5))
data <- data.frame(x = c(1000, 5000), ind = c(1, 3))

# mocking the shiny user info I was hoping for.
session <- list(groups = c("AppUsers", "Dept1"))

# in server.R. the previous data are reactive, so these are as well. 
data_scope <- filter(lookup, ad_group %in% session$groups) %>% pull(ind)

filtered_data <- filter(data, ind %in% data_scope)

# rest of app which consumes filtered_data

Instead, I ended up offloading the user privilege determination to the same rmd which stores the AD group mapping. Using a third data set, I can map user to AD group, then AD group to privilege.

Given the small number of users (100) and frequency of use, I think this is more than good enough for my purposes.


That's awesome! Well done! Impressive use of Connect's functionality to get around limitations in Connect's functionality :smile:

A couple of ideas to keep in mind (based on your pseudo-code, which is pretty nit-picky of me :sweat_smile: ):

  • If you end up using the pins package as you suggest, there is a pin_reactive() function that I only became aware of recently that creates the reactivePoll() behavior for you.
  • Accordingly, the "check" operation for pins is very cheap, so doing the check more often than every 15 minutes is super feasible

Exciting stuff! And thanks for sharing! We will definitely keep you posted when this functionality gets added to the product!

Haha thank you. I've found the RMarkdown output files solve a lot of problems well enough -- No need to make SQL tables for every little thing anymore, which I really appreciate!

Thanks for the advice. I was aware of switching pins_reactive(), but am waiting to try out pins in general on a new project. I'm not sure how much faster their check would be -- the
reactivePoll checkFunc I am using is something like httr::HEAD(url)[["last-modified-time"]].

You've also made me reconsider my check time -- I'll probably be moving it to 1 hour. I had forgotten the AD table only refreshes daily :sweat_smile:, so no need for even the 15 minute check. That, along with group membership turnover likely being on the order of 1-3 people a year, I think it'll be sufficient.

Again, I appreciate your helpfulness and advice!

1 Like

Oh brilliant! That's a very nice solution :smile: Yes, output files is definitely a wonderful solution if it makes sense for you! (the pins implementation is very much inspired by that idea). And the check cost will be similar - checking the last-modified-time on a HEAD request is basically costless.

Thanks for the great product feedback!! We're very glad to hear that you're on a happy path forward!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.