GrouperShell (gsh)
See also Grouper Custom Template via GSH
- 1 GrouperShell (gsh)
- 1.1 GSH operations
- 1.2 Hints and tricks
- 1.3 API Compability
- 1.4 Installation
- 1.5 Usage
- 1.6 Supported Commands
- 1.6.1 Grouper API methods
- 1.6.2 GrouperSessions
- 1.6.3 Group insert / update / delete
- 1.6.4 Groups
- 1.6.5 Group Types
- 1.6.6 New group types on folder
- 1.6.7 Set attribute on group
- 1.6.8 Member change subject
- 1.6.9 Memberships
- 1.6.10 Privileges
- 1.6.11 Registry
- 1.6.12 Stems
- 1.6.13 Subjects
- 1.6.14 System
- 1.6.15 Find bad memberships
- 1.6.16 Grouper export to GSH script
- 1.6.17 XML legacy
- 1.6.18 XML export legacy
- 1.6.19 XML import legacy
- 1.6.20 Transactions
- 1.6.21 Daemon
- 1.6.22 Loader
- 1.6.22.1 HSQLDB (similar example)
- 1.6.23 Jobs not firing in daemon
- 1.6.24 GrouperShell Variables (BeanShell only)
- 1.6.25 Membership scripts
- 1.6.26 Configuration in the database
- 1.6.27 Misc
- 1.6.28 External systems
- 1.6.29 Disable provisioning daemons, then re-enable them again
- 1.6.30 Create a script from SQL
- 1.7 Attribute framework
- 1.7.1 disableLoaders.gsh ( )
- 1.7.1.1 disableLoaders.gsh
- 1.7.1.2 Example of running disableLoaders.gsh
- 1.7.1 disableLoaders.gsh ( )
- 1.8 Rules
- 1.9 Grouper Builtin Messaging
- 1.10 Grouper messaging
- 1.11 Expression language testing
- 1.12 Example of attribute value assign to group and SQL query
- 1.13 Example of finding groups with a certain attribute value
- 1.14 Example of finding groups with a certain attribute value on metadata assignments
- 1.15 Example of an LDAP filter with LDAP debug
- 1.16 Example of finding provisioning targets for PSPNG
- 2 Include a common GSH file
- 3 Stem move
- 4 Check health of database connection or run a query
- 5 Set password using Grouper built-in authentication
- 6 Remove all group / folder privs for a user. Remove user from groups which have a privilege on another object
- 7 Delete all members from a subject source
- 8 Compile and run a main method without GSH
- 9 GSH script with environment variables
GrouperShell (gsh)
gsh is a command line shell for administering and interacting with the Grouper API. See . It can be used in both a batch and interactive manner. For Grouper 2.3.0 patch 72+, it is built on GroovyShell. For older versions of Grouper, it is built on Java BeanShell. The legacy BeanShell version is now deprecated, but you can switch back to it by using one of the options:
Setting gsh.useLegacy = true in grouper.properties.
Using a command line argument (gsh.sh -forceLegacyGsh)
GrouperShell is for Grouper admins. End users can script with the grouper client command line utility
GSH operations
NOTE: Some classes were added a later 2.5.x releases. Not all are documented as to when they were initially added.
Category | Subtype | Action | Class |
|---|---|---|---|
Attestation | Folders | insert / update / delete | |
Groups | insert / update / delete | ||
Attribute assignment | Attribute assignment | insert / update / delete | AttributeAssignToAssignmentSave (2.5.48+) |
Folders | insert / update / delete | ||
Group | insert / update / delete | ||
Attribute definition |
| insert / update / delete | |
Attribute name |
| insert / update / delete | |
Attribute value |
| insert / update / delete | |
Composite |
| insert /update / delete | |
| finder | ||
Email SMTP |
|
| |
Gc db access |
| gc db access | |
Grouper session |
|
| |
Group |
| insert / update / delete | |
| finder | ||
| copy | ||
gsh |
| gsh template exec | |
Http |
|
| |
Ldap |
| ldap session utils | |
Member |
| finder | |
Membership |
| insert / update / delete | |
| finder | ||
Password |
| insert / update / delete | |
Privilege inheritance | Attribute definitions | insert / update / delete | |
| Finder | ||
Folders | insert / update / delete | ||
| Finder | ||
Groups | insert / update / delete | ||
|
| Finder | |
Provisionable | Folders | finder | |
insert / update / delete | |||
Groups | finder | ||
insert / update / delete | |||
Stem |
| insert/update/delete | |
| finder | ||
| copy | ||
Subject |
| finder | |
Sync data to SQL table |
|
| GcTableSyncFromData |
Types | Folders | finder | |
insert / update / delete | |||
Groups | finder | ||
insert / update / delete |
Hints and tricks
Escape things in groovysh with single backslash. e.g.
attributeValueDelegate.assignValue(RuleUtils.ruleIfConditionElName(), "\${subject.sourceId != 'g:gsa'}");Check for null like this
${someVar ?: 'valueIfNull'}GSH does not like array constructors
FROM
Object[] row = new Object[] {emailFromOutsystems, pennkeyPerhaps};
TO (groovy)
Object[] row = [emailFromOutsystems, pennkeyPerhaps];
TO (java)
GrouperUtil.toArray(GrouperUtil.toList("a", "b"), String.class)Reset the shell after an error:
:c
Escape dollars, e.g. "${something}"
'$' + "{something}"Externalized text
if you add to externalized text (config in ui), just make a key, e.g. mySchoolEmailKey, then refer to it like this
import edu.internet2.middleware.grouper.cfg.text.GrouperTextContainer;
String template = GrouperTextContainer.textOrNull("mySchoolEmailKey");Do not have a method name which is the same as a variable name:
Caused by: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of method:GSH has trouble with switch statements that work in Java (possibly only with enums or blank default parts?). Best to probably avoid them and use if statements.
API Compability
gsh is now a core part of the Grouper API and so is always compatible with the current release.
Installation
When using the Grouper API source distribution, grouper.jar needs to be built before using gsh.sh for the first time:
cd $GROUPER_HOME
ant dist
Usage
For Windows use $GROUPER_HOME\bin\gsh.bat
Run gsh as an interactive shell:
$GROUPER_HOME/bin/gsh.sh
Read gsh commands from a script file:
$GROUPER_HOME/bin/gsh.sh /path/to/your/script.gsh
Run Grouper utilities:
$GROUPER_HOME/bin/gsh.sh <option> args: -h, Prints this message
args: <filename>, Execute commands in specified file
no args: Enters an interactive shell
args: -lightWeightProfile
Use alternate init script (classes/groovysh_lightWeight.profile)
which has less imports and may improve startup performance
args: -nocheck, Skips startup check and enters an
interactive shell
args: -runarg <command> Run command (use \\n to separate commands)
args: -main <class> [args...]
class, Full class name (must have main method)
args, args as required by main method of class
args: -initEnv [<configDir>]
On Windows sets GROUPER_HOME and adds GROUPER_HOME/bin to path
For *nix 'source gsh.sh' for the same result
configDir optionally adds an alternative conf directory than
GROUPER_HOME/conf to the classpath
args: (-xmlimport | -xmlexport | -loader | -test | -registry |
-findbadmemberships | -ldappc | pspngAttributesToProvisioningAttributes) Enter option to get additional usage for that
option
-xmlimport, Invokes XmlImporter*
*XML format has changed in v1.6. To import
the original XML format use -xmlimportold
-xmlexport, Invokes XmlExporter
-loader, Invokes GrouperLoader
-registry, Manipulate the Grouper schema and install
bootstrap data
-test, Run JUnit tests
-pspngAttributesToProvisioningAttributes Copies pspng attributes to provisioning
-findbadmemberships, Check for membership data inconsistencies
-ldappc, Run the grouper ldap provisioning connector to send data to ldap With argument "-lightWeightProfile", gsh will start up with an alternate boot script (groovysh_lightWeight.profile instead of the default groovysh.profile). This will perform on startup only a few Java imports, edu.internet2.middleware.grouper.* and import edu.internet2.middleware.grouper.util.*, and does not set up any help aliases and functions.
Note: you can log sql statements run from gsh by setting this in log4j.properties
log4j.logger.org.apache.tools.ant = WARNRun SQL file
./gsh.sh -registry -runsqlfile subjects.sqlIn GSH for Grouper 2.4 and above, to not print the value of every line, use this:
:set verbosity QUIETValid values for verbosity are DEBUG, VERBOSE, INFO (default), and QUIET.
If the temporary directory used by your JVM doesn't allow execution of executables (e.g. the directory has the noexec option set), then you may run into an error starting GSH. Try setting the following environment variable before starting GSH.
export GSH_JVMARGS="-Dlibrary.jansi.path=/some/other/temp/path/with/exec"Environment variables that affect GSH startup:
GROUPER_HOME: if set to a valid Grouper directory, it will use this directory. Otherwise, it will determine it based on the path to gsh
GROUPER_CONF: if set to a valid conf directory, it will use this directory. Otherwise it will determine it based on GROUPER_HOME
MEM_START: Override the default -Xms Java parameter (initial Java heap size)
MEM_MAX: Override the default -Xmx Java parameter (maximum Java heap size)
CLASSPATH: Will prepend to the constructed classpath
GSH_JVMARGS: Additional arguments to pass to Java
GSH_CYGWIN: (since 2.4.0 api patch 3) if set and not blank, the script will convert paths and the classpath to Windows-style, for use with Windows Java under Cygwin
GSH_QUIET: (since 2.4.0 api patch 3) if set and not blank, will not output preliminary diagnostic information before starting Java, other than errors
Command line arg in script
./gsh -runarg 'userToFind="user1"\n:load "/opt/grouper/scripts/myGSHScript.gsh"'
Supported Commands
Grouper API methods
Any Grouper API method can be directly invoked just by referencing it, inclusive of the class in which it is defined. Methods return a java object which can be stored in a variable. For example, the following gsh session determines all of the groups to which a given subject belongs:
gsh 0% GrouperSession.startRootSession();
gsh 0% subj = findSubject("SD00125")
subject: id='SD00125' type='person' source='kitn-person' name='Barton, Tom'
gsh 1% sess = GrouperSession.start(subj)
edu.internet2.middleware.grouper.GrouperSession: 29c40f97-9fb0-4e45-88bc-a14877a6c9b5,'SD00125','person'
gsh 2% member = MemberFinder.findBySubject(sess, subj)
member: id='SD00125' type='person' source='kitn-person' uuid='d0fa765e-1439-4701-89b1-9b08b4ce9daa'
gsh 3% member.getGroups()
group: name='etc:sysadmingroup' displayName='Grouper Administration:SysAdmin Group' uuid='6f77fb36-b466-481a-84a7-7af609f1ad09'
GrouperSessions
Group insert / update / delete
Groups
Command | Description |
|---|---|
getGroups(name) | Find all groups with a matching naming attribute value, returns a Set of groups When using Java 1.8+ and Grouper 2.3 (later patches)+ this can be handy to print the group.getName() values for all groups that are found. getGroups("Wheel").each{it -> println "${it.getName()}"} |
GroupFinder.findByName(grouperSession, name) | Find one group by name |
GroupFinder.findByUuid(grouperSession, name) | Find one group by uuid |
Group Types
New group types on folder
GrouperSession grouperSession = GrouperSession.startRootSession();
Stem stem = StemFinder.findByName(grouperSession, "test:gdg:app", true);
AttributeDefName typeMarker = AttributeDefNameFinder.findByName("etc:objectTypes:grouperObjectTypeMarker", true);
AttributeAssign attributeAssign = stem.getAttributeDelegate().hasAttribute(typeMarker) ? stem.getAttributeDelegate().retrieveAssignments(typeMarker).iterator().next() : stem.getAttributeDelegate().addAttribute(typeMarker).getAttributeAssign();
attributeAssign.getAttributeValueDelegate().assignValue("etc:objectTypes:grouperObjectTypeDirectAssignment", "true");
attributeAssign.getAttributeValueDelegate().assignValue("etc:objectTypes:grouperObjectTypeName", "app");Provisionable on folder with metadata
GrouperSession grouperSession = GrouperSession.startRootSession();
AttributeAssign attributeAssignMarker = null;
attributeAssignMarker = new AttributeAssignSave(grouperSession).assignOwnerStemName("test:chris:test").assignNameOfAttributeDefName("etc:provisioning:provisioningMarker").save();
new AttributeAssignSave(grouperSession).assignOwnerAttributeAssign(attributeAssignMarker).assignNameOfAttributeDefName("etc:provisioning:provisioningDirectAssign").addValue("true").save();
new AttributeAssignSave(grouperSession).assignOwnerAttributeAssign(attributeAssignMarker).assignNameOfAttributeDefName("etc:provisioning:provisioningDoProvision").addValue("ADTest").save();
new AttributeAssignSave(grouperSession).assignOwnerAttributeAssign(attributeAssignMarker).assignNameOfAttributeDefName("etc:provisioning:provisioningStemScope").addValue("sub").save();
new AttributeAssignSave(grouperSession).assignOwnerAttributeAssign(attributeAssignMarker).assignNameOfAttributeDefName("etc:provisioning:provisioningTarget").addValue("ADTest").save();
new AttributeAssignSave(grouperSession).assignOwnerAttributeAssign(attributeAssignMarker).assignNameOfAttributeDefName("etc:provisioning:provisioningMetadataJson").addValue("{\"md_trim_prefix\":\"whatever\",\"md_entityId\":\"theEntityId\"}").save();
Set attribute on group
Add a new type with an attribute, add this to group and then set its value on the group, e.g.
grouperSession = GrouperSession.startRootSession();
g = GroupFinder.findByName(grouperSession, "admin:loader_groups");
type = typeAdd("sync_group");
type.addAttribute(grouperSession, "sync_group", false, "sync");
groupAddType("admin:loader_groups", "sync_group");
g.setAttribute("sync", "true");Command | Description |
|---|---|
groupAddType(group name, type name) | Add type to group |
groupDelType(group name, type name) | Delete type from group |
groupGetTypes(group name) | Get group's types |
groupHasType(group name, type name) | Check whether group had type |
typeAdd(type name) | Create custom group type |
typeAddAttr(type name, attr name, read, write, required) | Create custom group attribute. read and write must be an |
typeAddList(type name, attr name, read, write) | Create a custom list. read and write must be an |
typeDel(type name) | Delete group type |
typeDelField(type name, field name) | Delete custom field from group type |
typeFind(type name) | Find the group |
typeGetFields(type name) | Get fields associated with the group type |
Member change subject
"Member change subject" will change the subject that a member refers to. You would want to do this when a person or entity changes their id, or if they were loaded wrong in the system. If the new subject does not have a member associated with it, this is a simple case, where the subject data is put in the member object. If the new subject does have a member object, then all data in all tables that referred to the old member object, will now refer to the new member object. The old member is deleted from the member table by default, though this is an option. Generally you will want it removed, unless there is a foreign key problem where you need to do as much work as possible. In GSH you can get a dry-run report of what will be done.
The operation is potentially time consuming only when two formerly separate Subjects are being merged into one, and that the time required is to replace the memberships (and audit fields e.g. modifiedBy) of the formerly separate Subject that is being retired with new ones associated with the other Subject.
grouperSession = GrouperSession.startRootSession();
oldSubject = findSubject("10021368");
member = MemberFinder.findBySubject(grouperSession, oldSubject);
newSubject = findSubject("10021366");
member.changeSubject(newSubject);
Command | Description |
|---|---|
member.changeSubject(newSubject); | Change the subject of the member object. If the subject is the same, its a no-op. If the new subject does not have a Member object, then the existing member object simply gets new subject information. If the new subject does have a member object, then all objects in the grouper registry which uses the old member, will be updated to the new member. Then the old member object is deleted from the registry |
member.changeSubject(newSubject,!Member.DELETE_OLD_MEMBER); | Change the subject, but dont delete the old member. Do this if the way which deletes the old member doesnt work due to foreign keys. This will do all the work it can, and the rest can be manual |
member.changeSubjectReport(newSubject,Member.DELETE_OLD_MEMBER); | Dont do any of the work, just print a report to the screen of what will be done. Dry-run. |
Memberships
Command | Description |
|---|---|
addComposite(group name, composite type, left group name, right group name) | Add composite membership. e.g. CompositeType.UNION |
addMember(group name, subject id) | Add member to the members list for the group. |
addMember(group name, subject id, field) | Add member to the specified list for the group. |
delComposite(group name) | Delete composite membership from group |
delMember(group name, subject id) | Delete member from the members list for the group |
delMember(group name, subject id, field) | Delete member from the specified list for the group |
getMembers(group name) | Get members of group |
hasMember(group name, subject id) | Check whether subject is member of the members list |
hasMember(group name, subject id, field) | Check whether subject is member of the specified list |
GrouperSession grouperSession = GrouperSession.startRootSession(); | Add member with subjectId and sourceId |
GrouperSession grouperSession = GrouperSession.startRootSession(); | Add member with subjectIdentifier and sourceId |
Privileges
Command | Description |
|---|---|
grantPriv(group name, subject id, privilege) | Grant privilege on group. privilege must be an AccessPrivilege (e.g. |
grantPriv(stem name, subject id, privilege) | Grant privilege on stem. privilege must be a NamingPrivilege (e.g. |
hasPriv(group name, subject id, privilege) | Check whether subject has privilege on group. privilege must be an AccessPrivilege (e.g. |
hasPriv(stem name, subject id, privilege) | Check whether subject has privilege on strem. privilege must be a NamingPrivilege (e.g. |
revokePriv(group name, subject id, privilege) | Revoke privilege on group. privilege must be an AccessPrivilege (e.g. |
revokePriv(stem name, subject id, privilege) | Revoke privilege on stem. privilege must be a NamingPrivilege (e.g. |
Note for this:
usduByMember(member, usdu.DELETE)This fails for me because groovy can't find any such definition for usdu.DELETE. I got around this by passing 1 in like this to delete the memberships:
usduByMember(member, 1)I doubt this gets much use, but it seems like it would be worth actually defining usdu.DELETE somewhere.