As SAS Usage Note 45094 clearly points out “Column-level security is not supported in SAS® Web Report Studio”. But, you have to build a web report that will display fields with sensitive information to one group of users but not to another. So, now what?
One possible solution would be to create 2 information maps and 2 web reports. The first information map would contain the sensitive data field and would be used to build a corresponding web report. Both the information map and web report would have metadata read access granted to the user group authorized to view the sensitive data. Conversely, the second information map would not contain the sensitive data field and therefore neither would its corresponding web report. This information map and web report would have metadata read access granted to the wider user audience. So, problem solved, right? Not so fast. What if your customer doesn’t want 2 information maps and 2 web reports? What if they have a hard requirement that there is only a single web report which will display the sensitive data to some users and hide it from others?
Let’s explore the use of SAS views as the information map data source. While we won’t be able to actually hide the sensitive data field, we certainly can mask its values with this approach. This way we can build a single information map pointing to the SAS view and then create a single web report from that information map.
There are a couple tricky pieces in this puzzle:
- How do we know the identity of the user viewing the web report?
Well, that depends on whether your environment is using pooled workspace servers or not. With pooled workspace servers, the SAS system option metauser can be parsed to retrieve the user ID. With non-pooled workspace servers, the SAS macro variable sysuserid does the trick. It’s probably a good idea to check both, just in case.
- How do we know if the user has authorization to view the sensitive data field?
Set up a security group in the SAS metadata called maskdata, for example. For members of this group, the sensitive data field will appear masked, blanked out, or whatever you decide is best. With this security group established, you should be able to write OMI code (Open Metadata Interface) which queries the metadata to check whether the user viewing the web report is a member of the maskdata group or not.
Here is the SAS view solution using the sample data set sashelp.class:
/* assumes that the name of the security group in the metadata is "maskdata" */
data out.class / view=out.class;
drop maskdata username memberuri memberName n numMembers rc;
retain maskdata 0 username;
/* determine group membership - only execute once */
if _n_=1 then do;
/* retrieve user ID viewing web report */
username=upcase(symget('sysuserid'));
/* If non-pooled workspace server sysuserid will return the id of the user viewing the web report. Otherwise, for pooled servers, it will return SASSRV.*/
if username eq 'SASSRV' then
/* For pooled servers, parse the value of the system option metauser to retrieve the user ID. */
username=scan(getoption('metauser'),1,'@');
/* Use OMI functions to retrieve metadata about users and groups */
length memberuri $1000;
length memberName $200;
/* all return strings must be initialized */
memberuri='';
/* get the number of user IDs which are members of the "maskdata" user group */
numMembers=metadata_getnasn("omsobj:IdentityGroup?@Name='" !! "maskdata" !! "'","MemberIdentities",1,memberuri);
/* Retrieve each member of "maskdata" and see if it matches the user ID viewing the web report. If it matches then set the flag maskdata to 1 and jump out of the loop. */
do n=1 to numMembers;
memberName='';
numMembers=metadata_getnasn("omsobj:IdentityGroup??@Name='" !! "maskdata" !! "'","MemberIdentities",n,memberuri);
rc=metadata_getattr(memberuri,'name',memberName);
memberName=upcase(memberName);
if strip(upcase(username)) eq memberName then do;
/* the user viewing the web report is not allowed to see the sensitive data */
/* flag them as a member of the "maskdata" group */
maskdata=1;
/* jump out of loop */
leave;
end;
end;
end;
/* read source table */
set sashelp.class;
/* if user is a member of the "maskdata" group, then mask sensitive data field value */
if maskdata then
sex='x';
run;