If you mistype one or more lines in GSH, type :c to clear all the lines since the last executed command.
To exit GSH, type :q
- Grouper wiki command reference for GSH
- Groovy syntax: https://groovy-lang.org/documentation.html
- Grouper API Javadoc: https://software.internet2.edu/grouper/doc/
- 2.5.x (active release branch) -> Grouper API -> Project Reports -> Javadoc
- 3rd Party site Baeldung - Groovy blog posts: https://www.baeldung.com/tag/groovy/
- 3rd Party site MrHaki - Groovy blog posts: https://blog.mrhaki.com/
Exercise 301.4.1 - Getting started
Start up your Grouper docker stack. You will want the UI running (log in as banderson) so you can see visually how the shell commands impact objects in Grouper.
With a container started and running, GSH can initiated by running:
On start, you’ll see some helpful information about your grouper environment that will look something like this:
Note that the Grouper version is indicated, information on what the connection string to the Grouper database is, where various configuration files are read from, and available subject sources. If your subject source is based on a database instead of ldap, you will see information on the connection string to that database too.
A friendly GroovyShell prompt greets us at the end (groovy:000>) and we are ready to start interacting with GSH. We’ll start by looking up information about the subject banderson.
Note that you do not need to declare your variables in GSH ahead of time and that GSH will output information about the last run command. This is helpful in validating that the banderson we found is in fact Bob Anderson, but let’s make sure that this is really Bob Anderson, the Grouper administrator, and not some other imposter Bob Anderson.
This time, we did not specify a variable at the end because we did not care to use the result for anything else. When we run that command though, we see an error:
GSH displays a friendly version of the problem (highlighted in red on most consoles) and we can quickly see that we just put in the wrong group name. The additional stack information is helpful if you ever need to report a problem out to the grouper-users email list or to the Grouper developers.
Let’s fix our mistake. Use the up arrow to go to your previous command to fix it up without having to retype the entire thing so it matches the command below:
There we go. The getMembers(groupName) method returns a list of Member objects representing the group's members. The result shows there is one member in the sys admins group. The output uses the object's toString() method to show a readable form of the members.
In this next exercise, we will use that subject we found to create a few folders, a group, and add some members.
Exercise 301.4.2 - Creating a folder/group structure
Before we get started manipulating anything in Grouper, we will need to start a session. There are two ways to accomplish this, the first is starting a root session:
Or you can start a session as a specific subject and act as that subject:
Note that we used subj from the above exercise where we looked up and found banderson. Let’s create a folder called tmp:
The arguments for that command are the folder id and then the friendly name of the folder. Now let’s create a folder within tmp:
We specified the folder to make this folder in, the id of the folder, and the friendly name of the folder. If you make a mistake, whether it is a missing argument, or that it cannot find something you referenced in an argument, GSH will help you out:
IMPORTANT: You are often acting as a super user in GSH, so do be careful with any commands to ensure you do not accidentally destroy any data in your Grouper environment. GSH will not prompt you if it is OK to delete something once you hit enter on the command.
Take a look in the UI (you will want to login as banderson for these exercises). We will see our new folder and subfolder created:
Note that the creator of the folder is Bob Anderson, because we started the Grouper session as banderson. If you started a root session, the creator would instead be GrouperSysAdmin.
Click on More Actions -> View audit log.
Note that the audit log shows that this folder was created through a GSH command. This can be very valuable in determining whether it was Bob Anderson logged in through the UI creating folders or someone acting as Bob Anderson via GSH that created the folder instead. We’ll look at an example audit log with both when we create a group next.
Back in your GSH window, let’s now create a group:
...and add a member to it:
GSH prints out true because it successfully added the member ‘jsmith’ to ‘tmp:subfolder:test_group’. Here is a case where it fails to add the membership:
In this example, we tried to add a subject that does not exist. GSH points us in the direction of where we went wrong.
(As a reminder, the arguments for all of these commands are available on the GSH wiki page)
Let’s take a look in the UI at our new group we just created:
While you are in the UI, go ahead and also add ‘banderson’ to the group too as a member. Once you have done that, take a look at the audit log for the group:
Note how even though both group member adds were done as Bob Anderson, we can see that the second add was down through the UI while the first was done as a GSH command.
Exercise 301.4.3 - Working with the API
In addition to help methods like addGroup() and addMember(), All of the Java public methods in the Grouper jar -- plus methods from all the other jars in the WEB-INF/lib directory – can be called from within GSH. Up to now, there have been simple helper methods to work with groups and subjects. However, there isn't a helper command to retrieve the group memberships for a subject. But we can access other API methods to get them.
In this case, the GSH wiki section membership scripts has some code that looks useful as a starting point. It uses methods in the MembershipFinder class to set up a finder, add a subject, and do a search. The search results will be a set of MembershipSubjectContainer objects, which wrap the membership information including the subject and group. Looping through the resulting collection, we will extract the group from each container object and print the name.
First, let's make sure our subj variable is still banderson.
Now we do the search to get the collection, and then loop through and print the groups found.
Note the addition of some Groovy syntax here. Groovy has type checking, which means that you can specify the type of a variable, to ensure you are getting back the type of object you expect. If you don't care about the type or don't feel like typing it in or looking it up in the API, "def" just indicates a generic object.
The example above shows Java-like syntax for looping through the collection. But Groovy has an alternate syntax that is less typing:
This is a common way to execute loops in Groovy. The "each" function take a block of code, setting the variable to each value in turn. Also note that the getXXX() methods from Java can be shortened to XXX, to save even more typing.
Exercise 301.4.4 - Automation
We have received a request from an application owner to:
- Create a folder app:foo:userGroups
- Given a list of user ids, create a group named as the id, but make the display name the user's first and last name (i.e., the LDAP "cn" attribute)
- Grant each user read and update permissions for their group
Depending on how many users are in the list, this is potentially tedious to do through the UI. All these steps can be automated with GSH. For the incoming ids, a data object can be created within the script and initialized. Depending on their needs, modifying this step to read data from a file can be a later enhancement.
The users' names don't need to be part of the incoming data. Because of how the LDAP subject source is configured, Grouper can access certain attributes from a subject.
First, set up the ids, and create the folder.
Do a quick test to make sure the subjects all resolve, and have a name field we can use.
Everything looks good, so do the loop again, but now create the groups and assign the privileges. The grantPriv helper command takes Privilege Java objects, in this case Privilege.READ and Privilege.UPDATE.
In the UI, verify the groups were created. In the Privileges tab, spot check that read/update privileges have been created for the user matching the group name.
(Extra) Exercise 301.4.5 - Build a Report (making a GSH script)
In this exercise, we are going create an automatically generated report of all memberships for something like a daily report to a security officer around campus who is interested in who has access to what for auditing purposes. GSH can help us make that pretty simple without having to write any sort of database query. We will be generating that report using GSH, but also looking at how to run a script in GSH without having to copy and paste all of the lines of the script.
Open your favorite text editor and copy in the following script:
Save your script (in this case called report.gsh) and here are two ways we can run the gsh script:
Copy the script in to the container and then run it:
$ docker cp report.gsh 301.4.1:/tmp/report.gsh
Open a shell into the container and switch to the tomcat user:
$ sudo -u tomcat /bin/bash
Run the report
$ bin/gsh.sh /tmp/report.gsh"
Note that data in containers is not persistent and it would be better to mount in a directory where you keep your GSH scripts.
The output shows very little other than the output for each command run:
We can see our output report by running the following:
$ docker exec -i 304.4.1 cat /tmp/out
|ddavis232||David Davis (ddavis232, faculty)||false||true|
|jpeterson243||James Peterson (jpeterson243, student)||false||true|
|lthompson225||Lexi Thompson (lthompson225, student)||false||true|
|nroberts214||Nancy Roberts (nroberts214, faculty)||false||true|
|ehenderson217||Eric Henderson (ehenderson217, student)||false||true|
|agasper233||Adian Gasper (agasper233, student)||false||true|
|mvales228||Maddie Vales (mvales228, student)||false||true|
|aprice205||Ava Price (aprice205, student)||false||true|
|jnielson201||Jeremy Nielson (jnielson201, student)||false||true|
|cmorrison212||Christina Morrison (cmorrison212, student)||false||true|
...and we see our beautiful tab separated report we can now ship off to whoever requested it.
You could execute your recurring GSH scripts either by calling the docker commands from cron, or perhaps creating a container based on tier-grouper that runs crond instead of any of the grouper components. Your container would be built with the crontab and you would be able to schedule any functionality you need with grouper components like GSH. But that is an exercise left to the reader (for now).
(Extra) Exercise 301.4.6 - Creating and Running a Grouper Loader Job
For this exercise, we are going to create a very simple loader job that loads all subjects from the grouper members database table and run it. While the UI does provide a very easy to use mechanism to create and run loader jobs, there are times where you may find it convenient to use GSH instead. GSH can be helpful if there are a bunch of loader groups you need to create and do not want to click through the UI for each one. Much of how to create Grouper Loader jobs in GSH is documented on the Grouper Loader wiki page.
First, create the group.
Now assign it the attributes needed to make it a loader job. This time, instead of writing one line at a time, try copying the entire block below in to GSH:
GSH handled the line feeds between commands and you should see each command run followed by ‘true’ printed after each line. In the next exercise we will show you how to run a block of commands as a script, which is the preferred way over copy/paste many lines at a time.
Now let’s dry run the job to see what it would do. Because dry run requires a group object and not the group id as a string we are going to put a function to find the group in as the argument to dry run command:
Note that variable called ‘session’ that we set back in the first exercise is needed for the GroupFinder functions. It seems that what this loader job will do is fine, so let’s run the job:
Verify in the UI that the membership count matches what GSH reported. Also check out under More->Loader that the settings are the same as if you were to configure a loader job in the UI
(Extra) Exercise 301.4.7 - Burn it Down
Sometimes there are folders in Grouper that you simply want to destroy everything under including all history that it ever existed. A good example of this are course enrollment groups for a particular semester that are likely a lot of data and that changed often during the semester. After a certain amount of time, these groups will be meaningless and can be destroyed (including all history) in order to free up some storage. We do not have any course enrollments in this exercise, so let’s destroy all of our hard work from Exercise 301 instead.
If you do not have GSH open, fire it up again and run the following:
The first argument specifies what stem we want to destroy. The second argument is set to true as a test only mode. The third argument specifies whether you want this also destroyed from all point in time data too. We see above that it would destroy all of our work today as expected. Let’s do it:
This may take a while if you have a very large set of folders/groups to delete. This can also be very dangerous since, as you can see, there was no confirmation asked when we ran the command above before it destroyed everything. We can look in the Grouper UI to verify that our work is no longer there:
You have now used GSH to manipulate folders, groups, and members in Grouper and even create a report that could be automated by running script on a regular basis through cron. Finally, we used GSH to efficiently destroy everything from a given folder. These were all basic examples, but we hope that you can begin to see the potential power for GSH commands in creating things like templates for new services, bulk adding/removing members, writing a script a script to bootstrap your development environment, etc. All of these training exercises are built out using GSH scripts.
Here is an example from the training environment that bootstraps the environment by building out a complete tree of folders and groups, adds members, grants privileges, creates a loader job, add attributes, creates a composite group and assigns Grouper rules.