You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 6 Next »

The grouper atlassian connector implements the atlassian access and profile providers (which are OpenSymphony interfaces, so other software which uses those interfaces might be able to use this as well).  The connector uses the grouperClient which uses Grouper REST web services.  Note that you can connect Atlassian products to LDAP, which can be provisioned from Grouper.  One difference between that and this connector is this connector is read/write.  This connector has been tested with Grouper 1.6 and Atlassian as of 12/2010 (e.g. Jira 4.2.1).  Note, this is new as of 12/2010, so the closer to that date the more experimental it is.

To set this up, check it out and build it:

[mchyzer@flash grouperAtlassian]$ svn co http://anonsvn.internet2.edu/svn/i2mi/branches/GROUPER_1_6_BRANCH/grouper-misc/grouperAtlassianConnector
[mchyzer@flash grouperAtlassian]$ cd grouperAtlassianConnector/
[mchyzer@flash grouperAtlassianConnector]$ ant

Take the grouper.client.example.properties in the grouperAtlassianConnector/conf/grouper.client.example.properties, and put the connector properties in your own grouper.client.properties:

################################
## Atlassian connector settings
################################

# put a grouper folder name that is the root for atlassian groups
atlassian.root =

# atlassian source to use (leave blank for all sources)
atlassian.subject.search.sourceId =

# atlassian search by id, identifier, or idOrIdentifer (idOrIdentifier is Grouper 2.0+)
atlassian.subject.search.subjectId = identifier

# number of minutes to cache reads (-1 for none, though this isnt recommended since
# atlassian makes a LOT of calls to the group service)
# defaults to 2
atlassian.cache.minutes = 2

# list all sources here, and how to get the atlassian id
atlassian.source.jdbc.sourceId = jdbc
# should be "id" or an attribute name to get the identifier for atlassian
atlassian.source.jdbc.idOrAttribute = loginid
# email attribute for this source (needed if using the ProfileProvider)
atlassian.source.jdbc.emailAttribute = EMAIL
# should be "name" or "description" or an attribute name to get the name for atlassian (needed if using the ProfileProvider)
atlassian.source.jdbc.nameAttribute = name

#atlassian name of group which has all users in it, e.g. jira-users
atlassian.usersGroup = jira-users

# grouper group name of all users that have ever been in atlassian (profile service has access to these).  Leave blank to
# just use the users group
atlassian.grouperAllUsersGroup =

# if all users must be in atlassian.grouperAllUsersGroup,
# or if lookups of old users can be done without having to be in this group
atlassian.requireGrouperAllUsersGroupForLookups = false

# groups which should be assigned to various privileges for new groups created in confluence
atlassian.updaters =
atlassian.admins =
atlassian.readers =

# pretend these memberships exist (e.g. to bootstrap or for users not in grouper)
atlassian.autoadd.administrators.groupname = jira-administrators
atlassian.autoadd.administrators.usernames = admin

atlassian.autoadd.users.groupname = jira-users
atlassian.autoadd.users.usernames = admin

# users not in idm, this is needed if using the profile provider
atlassian.autoadd.admin.user.id = admin
atlassian.autoadd.admin.user.name = Atlassian ADMIN
atlassian.autoadd.admin.user.email = you@yourschool.edu


#ignore calls on this user to the web service
atlassian.ws.users.to.ignore = admin

#put a valid subject id or identifier here for testing, and that user's email and name
atlassian.test.subjectIdOrIdentifier =
atlassian.test.email =
atlassian.test.name =

# if you are using the edu.internet2.middleware.grouperAtlassianConnector.GrouperLoggingAccessProviderWrapper
# to log an access provider, set the underlying class here
atlassian.logging.accessProvider.class = com.atlassian.jira.user.osuser.JiraOFBizAccessProvider

# if you are using the edu.internet2.middleware.grouperAtlassianConnector.GrouperLoggingAccessProviderWrapper
# to log an access provider, set the underlying class here
atlassian.logging.profileProvider.class = com.atlassian.jira.user.osuser.JiraOFBizProfileProvider

Configure the WS url, and authentication in the grouper.client.properties, as you normally would, here is a kerberos (cleansed) example

# url of web service, should include everything up to the first resource to access
# e.g. http://groups.school.edu:8090/grouper-ws/servicesRest
# e.g. https://groups.school.edu/grouper-ws/servicesRest
grouperClient.webService.url = https://groups.school.edu/grouper-ws/servicesRest

# kerberos principal used to connect to web service
grouperClient.webService.login = atlassianGrouper/server.school.edu

# password for shared secret authentication to web service
# or you can put a filename with an encrypted password
grouperClient.webService.password = /home/user/pass/atlassianGrouper.pass


Take the grouperAtlassianConnector/dist/grouperAtlassianConnector.jar, the grouperClient.jar, and put them in the jira edit-webapp/WEB-INF/lib dir.  Take the grouper.client.properties and put it in the jira edit-webapps/WEB-INF/classes dir.  Take the osuser.xml file that was in the jira WEB-INF/classes dir.  Comment out the existing access and profile (optional) providers, and configure the grouper one(s)

        <!--provider class="com.atlassian.jira.user.osuser.JiraOFBizProfileProvider">
                <property name="exclusive-access">true</property>
        </provider -->

        <provider class="edu.internet2.middleware.grouperAtlassianConnector.GrouperProfileProvider">
                <property name="exclusive-access">true</property>
        </provider>

        <!-- provider class="com.atlassian.jira.user.osuser.JiraOFBizAccessProvider">
                <property name="exclusive-access">true</property>
        </provider -->

        <provider class="edu.internet2.middleware.grouperAtlassianConnector.GrouperAccessProvider">
                <property name="exclusive-access">true</property>
        </provider>

Stop the jira tomcat, build the package, delete the webapps/jira dir in the tomcat, and start the jira tomcat again (note: these steps will vary on how you installed Jira, but in generally, you need the two jars, the client config, and the osuser.xml config in the right place in WEB-INF subdirs.

Migrate Jira groups and memberships

First step should be when exporting stuff from old jira to new, look at group names, and search and replace the old names to new names of groups you want to rename.  e.g. if there is a space of invalid char, you could replace with a dash or underscore or whatever you want to do.  Then import.

Note, you will need to migrate existing groups and memberships to grouper.  You can easily do this with some database scripts that create GSH scripts.  Here is an example of a group create script for mysql:

We are creating a script with a line for each group like this:

addGroup(parent stem name, extension, displayExtension)

Run a query like this against the atlassian schema:

SELECT CONCAT(CONCAT(CONCAT(CONCAT('addGroup("test:school:apps:atlassian:groups", "', groupname), '", "'), groupname), '");') AS gsh_script
FROM groupbase;


Run the resulting GSH script against grouper.

grouperSession = GrouperSession.startRootSession();
grantPriv("test:school:apps:atlassian:groups:some-group", "test:school:apps:atlassian:admin:admins", AccessPrivilege.ADMIN);
grantPriv("test:school:apps:atlassian:groups:another-group", "test:school:apps:atlassian:admin:admins", AccessPrivilege.ADMIN);

If you want to set privileges in grouper, then do a SQL script like this:

SELECT CONCAT(CONCAT('grantPriv("test:school:apps:atlassian:groups:', groupname), '", "test:school:apps:atlassian:admin:admins", AccessPrivilege.ADMIN);') AS gsh_script FROM groupbase
SELECT CONCAT(CONCAT('grantPriv("test:school:apps:atlassian:groups:', groupname), '", "test:school:apps:atlassian:admin:readers", AccessPrivilege.READ);') AS gsh_script FROM groupbase
SELECT CONCAT(CONCAT('grantPriv("test:school:apps:atlassian:groups:', groupname), '", "test:school:apps:atlassian:admin:updaters", AccessPrivilege.UPDATE);') AS gsh_script FROM groupbase

Then do the memberships:

addMember(group name, subject id)

Run thie sql script

SELECT CONCAT(CONCAT(CONCAT(CONCAT('addMember("test:isc:ait:apps:atlassian:groups:', group_name), '", "'), user_name), '");') AS gsh_script
FROM membershipbase WHERE user_name <> 'admin' AND user_name NOT LIKE '%.%' AND user_name NOT LIKE '%@%' AND user_name NOT LIKE '%\_%' ;

Once you are migrated, keep a backup of groupbase and membershipbase, and truncate those tables

Migrate Confluence groups and memberships

Logging

The connector has excellent logging, each method will log in DEBUG mode, including if it was cached, and how long the method call took.  Change this in the log4j.properties of jira

#log4j.logger.edu.internet2.middleware.grouperAtlassianConnector.GrouperAccessProvider = DEBUG
#log4j.logger.edu.internet2.middleware.grouperAtlassianConnector.GrouperLoggingProfileProviderWrapper = DEBUG
log4j.logger.edu.internet2.middleware.grouperAtlassianConnector.GrouperProfileProvider = DEBUG
#log4j.logger.edu.internet2.middleware.grouperAtlassianConnector.GrouperLoggingAccessProviderWrapper = DEBUG


The logs look like this:

2010-12-18 15:53:22,516 TP-Processor8 DEBUG admin 953x51x1 x4b0lo 1.2.3.4 /plugins/servlet/streams [internet2.middleware.grouperAtlassianConnector.GrouperProfileProvider] operation: getPropertySet, username: myuser, retrievedFromPropertySetCache: true, fullName: Chris Hyzer ADMIN, email: mchyzer@school.edu, timeMillis: 0
2010-12-18 15:53:22,516 TP-Processor8 DEBUG admin 953x51x1 x4b0lo 1.2.3.4 /plugins/servlet/streams [internet2.middleware.grouperAtlassianConnector.GrouperProfileProvider] operation: list, retrievedFromUserCache: true, resultList: Size 262: asmith, bsmith, csmith, dsmith..., timeMillis: 0
2010-12-18 15:53:22,516 TP-Processor8 DEBUG admin 953x51x1 x4b0lo 1.2.3.4 /plugins/servlet/streams [internet2.middleware.grouperAtlassianConnector.GrouperProfileProvider] operation: handles, username: myuser, retrievedFromPropertySetCache: true, fullName: Chris Hyzer ADMIN, email: mchyzer@school.edu, timeMillis: 0

Not, in the logs above, you see the wrapper providers, if you want to see how atlassian handles things, configure the atlassian default connector to be the provider in the grouper.client.properties, and configure the osuser.xml to point to the grouper wrapper, and it will print to the logs (INFO level) what the atlassian connector (or other connector) is doing.

Access provider

The atlassian root folder in grouper is where the atlassian groups are sandboxed.  I believe you could have descendants in that folder, put a group name in atlassian with a colon in it, but I havent tried it.  The access provider allows you to add / remove memberships from the Atlassian (e.g. Jira) admin console, or from Grouper.  You can create/delete groups in the atlassian UI also.  Note, obviously the WS user that atlassian uses needs access to the all the relevant groups.

Profile provider

This allows users id's, names, and emails to be retrieved from Grouper.  Note that if there is a user in Atlassian that is not resolvable in Grouper, that you would need to add it to the grouper.client.properties file as an autoadd user.  Note that users cannot be added/edited/deleted from the Atlassian admin console since Grouper does not control that.

Unit tests

Every method of the profile interfaces are unit tested.  To get these to work, you need to enter information in the grouper.client.properties (described above).  You can run these tests against a real installation and it will not negatively affect anything (shouldnt do this in prod though unless you are careful (smile) )

Caching

Atlassian calls methods frequently, so the connector does a lot of caching.  The default is to cache for 2 minutes.  If the write action is performed in Atlassian admin console, the caches are cleared.  Otherwise it could take 2 minutes for Grouper actions to propagate to Atlassian (or however you configure in grouper.client.properties)

sdf

  • No labels