PennIDTranslation

From Provider Wiki

Jump to: navigation, search



Contents

Applications or services requiring a user’s PennID for authorization decisions will be required to retrieve the PennID using the PennKey – PennID LDAP translation service (PennGroups).

Prerequisites

The service is available to any person with a PennKey, but the PennKey must be authorized. An application or entity that can use PennGroups can also use the translation service. To use the translation from a web service requires a kerberos service principal created by an administrator with kadmin privileges. Once the service principal is created, PennGroups must be configured to allow access for the service principal. As a temporary process, to register a service principal for PennGroups, please contact PennGroups Help[1].

Sample LDAP Invocations

Use the PennGroups client to test connectivity

Download the penngroups client:

http://www.upenn.edu/computing/penngroups/pennGroupsClient-1.4.2.zip

Unzip to a directory. You need java1.5+ (aka java5+) on the machine. Edit the top of the grouper.client.properties to put in your service principal and password:

 ########################################
 ## LDAP connection settings
 ########################################
 
 # url of directory, including the base DN (distinguished name)
 # e.g. ldap://server.school.edu/dc=school,dc=edu
 # e.g. ldaps://server.school.edu/dc=school,dc=edu
 grouperClient.ldap.url = ldaps://penngroups.upenn.edu/dc=upenn,dc=edu
 
 # kerberos principal used to connect to ldap
 # e.g. name/server.whatever.upenn.edu
 grouperClient.ldap.kerberosPrincipal = penngroups/medley.isc-seo.upenn.edu
 
 # password for shared secret authentication to ldap
 # or you can put a filename with an encrypted password
 grouperClient.ldap.password = xxxxxxx
 

Then try to do a conversion:

 C:\temp\pennGroupsClient-1.4.2>c:\dev_inst\java\bin\java -jar grouperClient.jar --operation=pennnameToPennid --pennnameToDecode=mchyzer
 pennid: 10021368
 
 C:\temp\pennGroupsClient-1.4.2>

Note, just use this command for more options: java -jar grouperClient.jar

Convert PennName to PennID

As user 'jorj', use my PennKey password to convert the pennname 'dakowitz' to a pennid:

 # ldapsearch -LLL -x -H ldaps://penngroups.upenn.edu/ -b "ou=pennnames,dc=upenn,dc=edu" -D
"uid=jorj,ou=entities,dc=upenn,dc=edu" -W pennname=dakowitz

Convert PennID to a PennName (I)

As user 'jorj', use my PennKey password to convert the pennid 10018604 to a name:

 # ldapsearch -LLL -x -H ldaps://penngroups.upenn.edu/ -b "ou=pennnames,dc=upenn,dc=edu" -D 
"uid=jorj,ou=entities,dc=upenn,dc=edu" -W pennid=10018604

Convert PennID to PennName (II)

Using kerberos credentials, convert the pennid 31653243 to a pennname:

 # ldapsearch -LLL -Y GSSAPI -H ldap://penngroups.upenn.edu/ -b "ou=pennnames,dc=upenn,dc=edu" pennid=31653243

Sample LDAP Invocations

Perl

Using GSSAPI (native Kerberos)

#!/usr/bin/perl

use strict;
use warnings;

use Net::LDAP;
use Authen::SASL;

my $lookup_pennname = shift (@ARGV) || die "No pennname given";


# empty callback so Net::LDAP doesn't override it 
my $sasl = Authen::SASL->new('GSSAPI', 
			     'user' => '');
my $ldap = Net::LDAP->new( 'penngroups.upenn.edu', 
			   version => 3 )
    || die "$@";

my $mesg = $ldap->bind( '', sasl => $sasl );

$mesg->code && die $mesg->error;
$mesg = $ldap->search(
    base   => 'ou=pennnames,dc=upenn,dc=edu',
    filter => "(&(pennname=${lookup_pennname})(objectClass=*))",
    attrs => [ 'pennname', 'pennid' ],
);

$mesg->code && die $mesg->error;

foreach my $entry ( $mesg->sorted('pennname') ) {
    print sprintf("%s: %s\n",
		    $entry->get_value('pennname'),
		  $entry->get_value('pennid'));


}

$ldap->unbind;

Using Username and Password

#!/usr/bin/perl

use strict;
use warnings;

use Net::LDAP;

my $lookup_pennname = shift (@ARGV) || die "No pennname given";



my $ldap = Net::LDAP->new( 'ldaps://penngroups.upenn.edu', 
			   version => 3 )
    || die "$@";

my $mesg = $ldap->bind( 'uid=<pennname>,ou=entities,dc=upenn,dc=edu',
			password => '<password>' );

$mesg->code && die $mesg->error;
$mesg = $ldap->search(
    base   => 'ou=pennnames,dc=upenn,dc=edu',
    filter => "(&(pennname=${lookup_pennname})(objectClass=*))",
    attrs => [ 'pennname', 'pennid' ],
);

$mesg->code && die $mesg->error;

foreach my $entry ( $mesg->sorted('pennname') ) {

    print sprintf("%s: %s\n",
		  $entry->get_value('pennname'),
		  $entry->get_value('pennid'));



}

$ldap->unbind;

PHP

<?php

$lookup_name = 'user';
$filter = "(&(pennname= " . $lookup_name . ")(objectClass=*))";
$pg_server = 'penngroups.upenn.edu';
$bind_dn = 'uid=<authorized_pennname>,ou=entities,dc=upenn,dc=edu';
$base_dn = 'ou=pennnames,dc=upenn,dc=edu';
$attrs = array( 'pennname', 'pennid' );

$lh = ldap_connect( $pg_server );
if ( !$lh ) {
    echo "ldap_connect failed: " . ldap_error( $lh );
    exit( 1 );
}
ldap_set_option( $lh, LDAP_OPT_PROTOCOL_VERSION, 3 );
ldap_set_option( $lh, LDAP_OPT_REFERRALS, 0 );
if ( !ldap_start_tls( $lh )) {
    echo "ldap_start_tls failed: " . ldap_error( $lh );
    exit( 1 );
}

if ( !ldap_bind( $lh, $bind_dn, "<password>")) {
    echo "ldap_bind failed: "  . ldap_error( $lh );
    exit( 1 );
}

$results = ldap_search( $lh, $base_dn, $filter, $attrs );
if ( !$results ) {
    echo "ldap_search failed: " . ldap_error( $lh );
    exit( 1 );
}

$entries = ldap_get_entries( $lh, $results );
if ( !$entries ) {
    echo "ldap_get_entries failed: " . ldap_error( $lh );
    exit( 1 );
}

for ( $i = 0; $i < $entries[ 'count' ]; $i++ ) {
    $entry = $entries[ $i ];
    for ( $j = 0; $j < $entry[ 'count' ]; $j++ ) {
	if ( !isset( $entry[ 'pennname' ][ $j ] ) ||
		!isset( $entry[ 'pennid' ][ $j ] )) {
	    continue;
	}
	echo $entry[ 'pennname' ][ $j ] . ": " .
		$entry[ 'pennid' ][ $j ] . "\n";
    }
}

// this closes the connection, too.
ldap_unbind( $lh );

exit( 0 );

?>

Java

import java.util.Hashtable;
import java.util.Enumeration;
import java.net.URLEncoder;
import javax.naming.*;
import javax.naming.directory.*;

public class PennkeyPennIDConverter
{
    public static String initialContext = "com.sun.jndi.ldap.LdapCtxFactory";
    public static String pennGroupsServer = "ldaps://penngroups.upenn.edu";
    public static String searchBase = "ou=pennnames,dc=upenn,dc=edu";
    public static String bindDN = "uid=<AUTHORIZED USERNAME>,ou=entities,dc=upenn,dc=edu";
    public static String bindPassword = "password";

    public static void main( String args[] )
    {
	try {
	    Hashtable<String,String>	env = new Hashtable<String,String>();
	    String			lookupUser;
	    String			filter;
	    DirContext			pennGroupsContext;
	    SearchControls		controls;
	    NamingEnumeration		results;

	    lookupUser = URLEncoder.encode( args[0], "UTF-8" );
	    filter = "(&(pennname=" + lookupUser + ")(objectClass=*))";

	    env.put( Context.INITIAL_CONTEXT_FACTORY, initialContext );
	    env.put( Context.PROVIDER_URL, pennGroupsServer );
	    env.put( Context.SECURITY_AUTHENTICATION, "simple" );
	    env.put( Context.SECURITY_PRINCIPAL, bindDN );
	    env.put( Context.SECURITY_CREDENTIALS, bindPassword );

	    pennGroupsContext = new InitialDirContext( env );
	    controls = new SearchControls();
	    controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
	    controls.setReturningAttributes( new String[] { "pennname",
								"pennid" } );

	    results = pennGroupsContext.search( searchBase, filter, controls );
	    while ( results != null && results.hasMore()) {
		SearchResult		r = (SearchResult)results.next();
		Attributes		attrs = r.getAttributes();
		String			pennname, pennid;

		pennname = (String)attrs.get( "pennname" ).get();
		pennid = (String)attrs.get( "pennid" ).get();
		System.out.println( pennname + ": " + pennid );
	    }
	} catch ( Exception e ) {
	    e.printStackTrace();
	    System.exit( 1 );
	}

	System.exit( 0 );
    }
}


ColdFusion

Download the PennGroups.doc file (Media:PennGroups.doc) and copy the code making a PennGroups.cfc. You will instantiate an object using your Kerberos Principal and Password. Results will be limited to your account's access level. Below is example code to setup and execute actions against the cfc.

<cfset localStruct.CFC_PATH ="PATH_TO_PENNGROUPS" />
<cfset localStruct.k_username ="YOUR_KERBEROS_PRINCIPAL" />
<cfset localStruct.k_password ="YOUR_KERBEROS_PASSWORD" />
<!--- a valid non Temp Employee --->
<cfset localStruct.penn_id="YOUR_PENN_ID"/>
<cfset localStruct.pennName="YOUR_PENNNAME"/>

<cfset PennGroupsObj = CreateObject("component", "#localStruct.CFC_PATH#").Init(ldapUser="#localStruct.k_username#", 
ldapPassword="#localStruct.k_password#") 
/>
<cfsetting requesttimeout="180" >
<cfoutput>
<h2>Lookup Penn_Id</h2>
<p>Penn_Id should be #localStruct.penn_id#:	 
	<cfdump var="#PennGroupsObj.getPenn_ID(pennName='#localStruct.pennName#')#" />
</p>
<p>Using invalid PennName, Penn_Id should be Empty String:	 
	<cfdump var="#PennGroupsObj.getPenn_ID(pennName='adfasdf#localStruct.pennName#123123')#" />
</p>

<h2>Lookup PennName</h2>
<p>PennName should be #localStruct.pennName#:  
	<cfdump var="#PennGroupsObj.getPennName(penn_id='#localStruct.penn_id#')#" />
</p>

<h2>Is #localStruct.pennName# a member of the group employeeNonTemp</h2>
<p>isMember True of cn=penn:community:employeeNonTemp :  
	<cfdump var="#PennGroupsObj.isMember(pennName='#localStruct.pennName#',groupName='cn=penn:community:employeeNonTemp')#" />
</p>
<p>isMember False of cn=penn:community:employeeNonTemp :  
	<cfdump var="#PennGroupsObj.isMember(pennName='adsfadsf#localStruct.pennName#123423423',
	groupName='cn=penn:community:employeeNonTemp')#" 
	/>
</p>

<h2>Get all members of employeeNonTemp</h2>
<p>getMembers of cn=penn:community:employeeNonTemp : 
	<cfdump var="#PennGroupsObj.getMembers(groupName='cn=penn:community:employeeNonTemp')#" />
</p>

<h2>Get all Groups #localStruct.k_username# can see</h2>
<p>ListAllGroups in Groups OU : 
	<cfdump var="#PennGroupsObj.getAllGroups()#" />
</p>

<h2>Get all groups by a PennName</h2>
<p>GetAllGroupsByMember using #localStruct.pennName# :  
	<cfdump var="#PennGroupsObj.getAllGroupsByMember(pennName='#localStruct.pennName#')#" />
</p>
<p>GetAllGroupsByMember invalid PennName,  :  
	<cfdump var="#PennGroupsObj.getAllGroupsByMember(pennName='123123#localStruct.pennName#123123')#" />
</p>

</cfoutput>



ASP.NET (C#)

<%@ Page Language="C#" %>
<%@ assembly name="System.DirectoryServices, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %>
<%@ import Namespace="System.DirectoryServices" %>
<%@ import Namespace="System.Text.RegularExpressions" %>
<script runat="server">

/*
 Change ldapUsername and ldapPassword to match
 your registered Kerberos principal and password
*/
string ldapUsername = ""; //Example: ldapUsername = “uid=myPennName,ou=entities,dc=upenn,dc=edu”;
string ldapPassword = "";

// DO NOT MAKE CHANGES BELOW THIS LINE
string pennKey = "[PennKey not available in REMOTE_USER header.]";
string pennId = "[Not retrieved.]";

void Page_Load(Object sender, EventArgs args) {

	if ( !isNullOrEmpty(Request.Headers["REMOTE_USER"]) && !isNullOrEmpty(Request.Headers["COSIGN_SERVICE"]) ) {
		pennKey = Request.Headers["REMOTE_USER"];

		if ( !Regex.IsMatch(pennKey, @"^[A-Za-z][A-Za-z0-9]{1,7}$") ) {
			pennId = "[PennID not retrieved. PennKey unsafe for use in LDAP query.]";
			return;
		}

		if ( isNullOrEmpty (ldapUsername) || isNullOrEmpty(ldapPassword) ) {
			pennId = "[PennID not retrieved. LDAP credentials not set.]";
			return;
		}

		try {
			DirectoryEntry entry = new DirectoryEntry("LDAP://penngroups.upenn.edu/ou=pennnames,dc=upenn,dc=edu");
			entry.AuthenticationType = AuthenticationTypes.Encryption;
			entry.Username = ldapUsername;
			entry.Password = ldapPassword;

			DirectorySearcher searcher = new DirectorySearcher(entry);
			searcher.Filter = "pennname=" + Request.Headers["REMOTE_USER"];
			SearchResult result = searcher.FindOne();

			if (result != null ) {
				ResultPropertyCollection coll = result.Properties;
				pennId = result.Properties["pennid"][0].ToString();
			}
		}
		catch (Exception e) {
			pennId = "[Exception occurred during retrieval. LDAP username and password likely unset.]";
		}
	}

}

bool isNullOrEmpty ( string str ) {
	return (str == null || str.Length == 0);
}
</script>
<html>
<head>
<title></title>
</head>
<body>
<p>
<%
	Response.Write("PennKey: " + Server.HtmlEncode( pennKey ) + "<br/>");
	Response.Write("PennID: "  + Server.HtmlEncode( pennId  ) );
%>
</p>
</body>
</html>

Additional Information on PennGroups

http://prowiki.isc.upenn.edu/wiki/PennGroups

The Pennkey to pennid translation currently updates data once per day. Note this is scheduled to be fixed in March 2010. Until then, please use the penngroups web service to translate pennkeys to pennids if ldap doesnt have the information. PennGroups will soon update once per hour. You can do that command line like this (this is an example, setup a group with no members that your principal can READ):

 [mchyzer@flash pennGroupsClient-1.4.2]$ java -jar grouperClient.jar --operation=hasMemberWs --groupName=penn:uphs:apps:pennNameToPennId:pennNameToPennId --pennKeys=mchyzer --outputTemplate='${wsSubject.id}$newline$' --sourceIds=pennperson
 10021368
 [mchyzer@flash pennGroupsClient-1.4.2]$ 

If you are using web service, it looks like this:

 [mchyzer@flash pennGroupsClient-1.4.2]$ java -jar grouperClient.jar --operation=hasMemberWs --groupName=penn:uphs:apps:pennNameToPennId:pennNameToPennId --pennKeys=mchyzer --outputTemplate='${wsSubject.id}$newline$' --sourceIds=pennperson --debug=true
 DEBUG: Reading resource: grouper.client.properties, from: /home/mchyzer/grouper/pennGroupsClient-1.4.2/grouper.client.properties
 DEBUG: WebService: connecting as user: 'penngroups/medley.isc-seo.upenn.edu'
 DEBUG: WebService: connecting to URL: 'https://medley10.isc-seo.upenn.edu/grouperWs/servicesRest/v1_4_000/groups/penn%3Auphs%3Aapps%3ApennNameToPennId%3ApennNameToPennId/members'
 
 ################ REQUEST START (indented) ###############
 
 POST /grouperWs/servicesRest/v1_4_000/groups/penn:uphs:apps:pennNameToPennId:pennNameToPennId/members HTTP/1.1
 Connection: close
 Authorization: Basic xxxxxxxxxxxxxxxx
 User-Agent: Jakarta Commons-HttpClient/3.1
 Host: medley10.isc-seo.upenn.edu:-1
 Content-Length: 310
 Content-Type: text/xml; charset=UTF-8
 
 <WsRestHasMemberRequest>
   <wsGroupLookup>
     <groupName>penn:uphs:apps:pennNameToPennId:pennNameToPennId</groupName>
   </wsGroupLookup>
   <subjectLookups>
     <WsSubjectLookup>
       <subjectIdentifier>mchyzer</subjectIdentifier>
       <subjectSourceId>pennperson</subjectSourceId>
     </WsSubjectLookup>
   </subjectLookups>
 </WsRestHasMemberRequest>
 
 ################ REQUEST END ###############
 
 
 
 ################ RESPONSE START (indented) ###############
 
 HTTP/1.1 200 OK
 Date: Wed, 23 Dec 2009 13:28:06 GMT
 Server: Apache/2.2.13 (Unix) DAV/2 PHP/5.2.11 mod_jk/1.2.28 mod_ssl/2.2.13 OpenSSL/0.9.8e-fips-rhel5
 Set-Cookie: JSESSIONID=xxxxxxxxxxxx; Secure
 X-Grouper-resultCode: SUCCESS
 X-Grouper-success: T
 X-Grouper-resultCode2: NONE
 Content-Length: 1378
 Connection: close
 Content-Type: text/xml
 
 <WsHasMemberResults>
   <results>
     <WsHasMemberResult>
       <wsSubject>
         <identifierLookup>mchyzer</identifierLookup>
         <resultCode>SUCCESS</resultCode>
         <success>T</success>
         <id>10021368</id>
         <sourceId>pennperson</sourceId>
       </wsSubject>
       <resultMetadata>
         <resultCode>IS_NOT_MEMBER</resultCode>
         <success>T</success>
       </resultMetadata>
     </WsHasMemberResult>
   </results>
   <wsGroup>
     <extension>pennNameToPennId</extension>
     <displayExtension>pennNameToPennId</displayExtension>
     <description>This group contains no members, but can be queried to get results which can translate pennkey/pennid</description>
     <displayName>penn:uphs:apps:pennNameToPennId:pennNameToPennId</displayName>
     <name>penn:uphs:apps:pennNameToPennId:pennNameToPennId</name>
     <uuid>db38d4e84404428a93fac3c528271f95</uuid>
   </wsGroup>
   <resultMetadata>
     <resultCode>SUCCESS</resultCode>
     <resultMessage>Success for: clientVersion: v1_4_000, wsGroupLookup: WsGroupLookup[groupName=penn:uphs:apps:pennNameToPennId:pennNameToPennId], subjectLookups: Array size: 1: [0]: WsSubjectLookup[subjectIdentifier=mchyzer,subjectSourceId=pennperson]
 
  memberFilter: All, actAsSubject: null, fieldName: null, includeGroupDetail: false, includeSubjectDetail: false, subjectAttributeNames: null
 ,params: null
 </resultMessage>
     <success>T</success>
   </resultMetadata>
   <responseMetadata>
     <millis>195</millis>
     <serverVersion>v1_4_002</serverVersion>
   </responseMetadata>
 </WsHasMemberResults>
 
 ################ RESPONSE END ###############
 
 
 DEBUG: Output template: ${wsSubject.id}
 , available variables: wsHasMemberResults, grouperClientUtils, index, wsGroup, wsHasMemberResult, wsSubject, resultMetadata, hasMember
 10021368
 DEBUG: Elapsed time: 1918ms
 [mchyzer@flash pennGroupsClient-1.4.2]$
Personal tools