May 20, 2010

Using Atlassian Crowd for Perforce Authentication

What's New
Integration

Atlassian Crowd can be used as an authentication provider for Perforce, using an auth-check trigger. Other enhancements are possible.Atlassian Crowd is a popular identity management solution, offering authentication, group management, and single sign-on (SSO) for several applications. Crowd lets us centrally manage authentication and access control for several applications -- who is allowed to authenticate, what they can see and do, and how we verify their password. By using Crowd as an authentication provider for Perforce, we automatically gain Crowd's ability to communicate with popular directories such as Active Directory.

First we'll consider using Crowd as an authentication provider. Before we dive into the details, note that we'll need to register Perforce as an application that Crowd recognizes. This process, described more fully at the Crowd documentation, determines which directories, groups, and users are allowed to access Perforce via Crowd.

We'll use an auth-check trigger to perform a password check with Crowd. The example script is written in Python, and communicates with Crowd via its SOAP API. The trigger uses the SUDS Python SOAP library, which is not part of the standard Python distribution.

#!/usr/bin/python

import sys
import os
import logging
import suds.client

# Note that we use some Unix directory conventions in this script...

# Normally we'd just point directly to the Crowd URL,
# but the Crowd WSDL has some errors that we had
# to fix in a local copy.
# See http://jira.atlassian.com/browse/CWD-159.
url='file:///tmp/SecurityServer.xml'

# create SOAP client
client = suds.client.Client(url)

# authenticate Perforce as a Crowd application
# (Every Perforce instance connecting to Crowd has to
# be registered and have a unique application name)
auth_context =
    client.factory.create('ns1:ApplicationAuthenticationContext')
auth_context.name = 'perforce'
auth_context.credential.credential = 'perforce'
token = client.service.authenticateApplication(auth_context)

# authenticate user, reading Perforce user name from command line
# and password from STDIN
passwordCredential = client.factory.create('ns1:PasswordCredential')
passwordCredential.credential = sys.stdin.readline()
authenticateContext =
    client.factory.create('ns1:UserAuthenticationContext')
authenticateContext.application = 'perforce'
authenticateContext.name = sys.argv[1]
authenticateContext.credential = passwordCredential
try:
    authtoken = client.service.authenticatePrincipal(token,
                                authenticateContext)
    sys.exit(0)
except Exception:
    print "Login failed"
    sys.exit(1)

Note that the trigger has to first identify Perforce to Crowd. (Crowd returns an application authentication token, normally valid for one hour. We can either obtain this token every time the trigger runs, or store it until it expires. An authentication token is unique per Perforce server instance.) Once we've authenticated Perforce as a registered Crowd application, we can perform the password check.

One caveat -- using Crowd as the authentication provider implies that we need Crowd online whenever our users have to authenticate. If you go down this route, make sure that you treat Crowd as a critical part of your Perforce infrastructure.

Possible Enhancements

Here are a few ideas for enhancing the use of Crowd with Perforce.

  • We may want to provide a way for at least one local super-user account to log in to Perforce without accessing Crowd. That would require a simple modification to our trigger, where we bypass the Crowd check for users who are named in a protected configuration file.
  • If we allow Crowd to change passwords in the directory, we can also provide an auth-set trigger so that users can change their passwords directly through Perforce. Users usually have a way to set their passwords at a higher level in LDAP or AD, so the use of an auth-set trigger is not common.
  • We can write a Crowd listener plugin that synchronizes Crowd users and groups with Perforce's internal user and group database. For instance, the listener would create a Perforce user when a user is added in Crowd. Depending on your environment, this solution could quickly become complex. We'd have to consider details like how to set the default ticket timeout for groups. Of course, managing the protections table is still purely a Perforce function.
  • Using the undocumented auth-check-sso trigger, we could try to enable single sign-on (SSO) for Perforce. Essentially, using SSO in Perforce requires two parts. First we need a script on the client side that can find an existing authentication token to pass to the server. On the server side, the trigger validates this token. The two complicating factors here are the requirement for a client side component for all users, and the fact that Crowd is normally used for SSO for web applications. It typically writes an authentication token into the browser cookie cache, which all Crowd-enabled web applications can find and use. Since Perforce is not a web application, it's an open question how a script could find the Crowd token. Perhaps someone out there knows the answer?