GrouperShell (gsh)

GrouperShell (gsh)

See also Grouper Custom Template via GSH

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

AttestationStemSave

Groups

insert / update / delete

AttestationGroupSave

Attribute assignment

Attribute assignment

insert / update / delete

AttributeAssignToAssignmentSave (2.5.48+)

Folders

insert / update / delete

AttributeAssignToStemSave

Group

insert / update / delete

AttributeAssignToGroupSave

Attribute definition

 

insert / update / delete

AttributeDefSave

Attribute name

 

insert / update / delete

AttributeDefNameSave

Attribute value

 

insert / update / delete

AttributeAssignValueSave

Composite

 

insert /update / delete

CompositeSave

 

finder

CompositeFinder

Email SMTP

 

 

GrouperEmail

Gc db access

 

gc db access

GcDbAccess

Grouper session

 

 

GrouperSession

Group

 

insert / update / delete

GroupSave

 

finder

GroupFinder

 

copy

GroupCopy

gsh

 

gsh template exec

GshTemplateExec

Http

 

 

GrouperHttpClient

Ldap

 

ldap session utils

LdapSessionUtils

Member

 

finder

MemberFinder

Membership

 

insert / update / delete

MembershipSave

 

finder

MembershipFinder

Password

 

insert / update / delete

GrouperPasswordSave

Privilege inheritance

Attribute definitions

insert / update / delete

PrivilegeAttributeDefInheritanceSave

 

Finder

PrivilegeAttributeDefInheritanceFinder

Folders

insert / update / delete

PrivilegeStemInheritanceSave

 

Finder

PrivilegeStemInheritanceFinder

Groups

insert / update / delete

PrivilegeGroupInheritanceSave

 

 

Finder

PrivilegeGroupInheritanceFinder

Provisionable


Folders

finder

ProvisionableStemFinder

insert / update / delete

ProvisionableStemSave

Groups

finder

ProvisionableGroupFinder

insert / update / delete

ProvisionableGroupSave

Stem

 

insert/update/delete

StemSave

 

finder

StemFinder

 

copy

StemCopy

Subject

 

finder

SubjectFinder

Sync data to SQL table

 

 

GcTableSyncFromData

Types

Folders

finder

GdgTypeStemFinder

insert / update / delete

GdgTypeStemSave

Groups

finder

GdgTypeGroupFinder

insert / update / delete

GdgTypeGroupSave

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 = WARN

Run SQL file

./gsh.sh -registry -runsqlfile subjects.sql

In GSH for Grouper 2.4 and above, to not print the value of every line, use this:

:set verbosity QUIET

Valid 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 AccessPrivilege (e.g. AccessPrivilege.ADMIN)

typeAddList(type name, attr name, read, write)

Create a custom list. read and write must be an AccessPrivilege (e.g. AccessPrivilege.ADMIN).

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();
Group group = GroupFinder.findByName(grouperSession, "a:b:c", true);
group.addMember(SubjectFinder.findByIdAndSource("someId", "sourceId", true), false);

Add member with subjectId and sourceId

GrouperSession grouperSession = GrouperSession.startRootSession();
Group group = GroupFinder.findByName(grouperSession, "a:b:c", true);
group.addMember(SubjectFinder.findByIdentifierAndSource("someIdentifier", "sourceId", true), false);

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. AccessPrivilege.ADMIN)

grantPriv(stem name, subject id, privilege)

Grant privilege on stem. privilege must be a NamingPrivilege (e.g. NamingPrivilege.STEM)

hasPriv(group name, subject id, privilege)

Check whether subject has privilege on group. privilege must be an AccessPrivilege (e.g. AccessPrivilege.ADMIN)

hasPriv(stem name, subject id, privilege)

Check whether subject has privilege on strem. privilege must be a NamingPrivilege (e.g. NamingPrivilege.STEM)

revokePriv(group name, subject id, privilege)

Revoke privilege on group. privilege must be an AccessPrivilege (e.g. AccessPrivilege.ADMIN)

revokePriv(stem name, subject id, privilege)

Revoke privilege on stem. privilege must be a NamingPrivilege (e.g. NamingPrivilege.STEM)

Comments