Novell Home

Managing LDAP User Attributes Dynamically through Group Membership

Novell Cool Solutions: Feature
By Mikko Mallat

Digg This - Slashdot This

Posted: 20 Dec 2006
 

Introduction

This article describes how to manage user attributes by using groups and Identity Manager edir-edir synchronization between two trees.

The goal is to build rules that add an LDAP application-specific attribute to user when a group is added, using common helpdesk tools like ConsoleOne. When removing the group, the attribute must be cleared.

Scenario

In this case we have two eDirectory trees. One of them is our production tree and other is an LDAP tree, giving applications access to data through LDAP. As applications become more and more LDAP-aware, we implemented some of our own custom attributes for these applications to use. These attributes contain, for example, a profile name that is read by an application during sign-in.

Problem

At first we did a mapping from production tree group to the LDAP attribute with an XSLT document. We used static XSLT, which we had to modify every time a new application profile was introduced. For a small amount of profiles this was sufficient, but the number of profiles and LDAP-aware applications kept growing, and so did the XSLT file. In addition, there was always some latency when the sysadmin took his time to update the new rules in XSLT. A bigger XSLT file meant that more CPU cycles were needed to process large "xsl:when test" code. When the profile/app count got out of hand, it wasn't cool anymore to handle these masses with static XSLT.

Solution

A better solution was needed. This was accomplished by using Policy Builder, some Xpath, a few local variables, and a common naming standard across group names and attribute values. In this example we have two applications: App-1 uses ldap attribute for profiling; App-2 uses attribute to determine whether user have access rights to application or not.

I wrote a policy that sniffs the group name, cuts it, and puts it into the profile attribute. It's quite simple but effective.

Also, I did some additional rules to handle things like what to do when account is disabled, etc.

This solution straightened up the process and cut down processing time. User administration tasks are done in helpdesk, as I played myself out of this "daily" routine ...

Figure 1 - OU structure

Directory Specifications

Company001

  • It holds the application profiling groups in groups.xx.company001.
  • These group objects are synchronized to ldap tree.
  • The common naming standard for these group objects is app-"groupname".
  • When a new group (profile) is added, static XSLT modification is no longer necessary. The policy calculates the correct profile name from group name.

LDAP

  • Schema extensions for ldap applications - Company:App1, Company:App2.
  • LDAP-aware applications check for permissions/profiles against these attributes.
  • App-1 uses the Company:App1 attribute and sets correct profile defined in ?groupname?
  • App-2 uses the Company:App2 attribute for permission checking; the attribute value can be true or false.
  • The Policy is defined in the Publisher's input transform policies.

Application Policy

In certain cases it was possible to use App1 even if the account was disabled; therefore, additional security measurements were needed. The first two rules of the policy take care of this and reformat the profile name with a "dis-" prefix if the account is disabled. This causes App-1 to fail, because it doesn't match any of its internal profile names. So, login to the application is no longer possible for that specific user. This prefix is removed from the destination value if the account is later enabled.

Local variables were used to speed things up by keeping the results of repeated queries in memory. Use of Xpath was necessary: when removing a group membership from a user, the op-attr "group membership" was empty. As you can see in the rules of App-1, the profile name is cut from the group name, so therefore it's important that group and profile naming uses a common naming standard. App-2 rules set the destination LDAP attribute to "true" or "false," depending on group membership. If you decide to implement this policy in your own environment, be sure to set the <token-substring length="64" start="32"> start point in rule 4 to match your environment.

Sample Code

<?xml version="1.0" encoding="UTF-8"?><policy xmlns:jstring="http://www.novell.com/nxsl/java/java.lang.String">
	<rule>
		<description>1. Reformat Company:app1 attribute if account is disabled</description>
		<conditions>
			<and>
				<if-class-name op="equal">User</if-class-name>
				<if-op-attr name="Login Disabled" op="changing-to">true</if-op-attr>
				<if-dest-attr name="Company:app1" op="available"/>
			</and>
		</conditions>
		<actions>
			<do-set-local-variable name="current-app1">
				<arg-string>
					<token-dest-attr name="Company:app1"/>
				</arg-string>
			</do-set-local-variable>
			<do-set-dest-attr-value name="Company:app1">
				<arg-value type="string">
					<token-text xml:space="preserve">dis-</token-text>
					<token-local-variable name="current-app1"/>
				</arg-value>
			</do-set-dest-attr-value>
			<do-break/>
		</actions>
	</rule>
	<rule>
		<description>2. Reformat Company:app1 attribute if account is enabled</description>
		<conditions>
			<and>
				<if-class-name op="equal">User</if-class-name>
				<if-op-attr name="Login Disabled" op="changing-to">false</if-op-attr>
				<if-dest-attr name="Company:app1" op="available"/>
			</and>
		</conditions>
		<actions>
			<do-set-local-variable name="enable-app1">
				<arg-string>
					<token-dest-attr name="Company:app1"/>
				</arg-string>
			</do-set-local-variable>
			<do-set-dest-attr-value name="Company:app1">
				<arg-value type="string">
					<token-substring length="64" start="4">
						<token-local-variable name="enable-app1"/>
					</token-substring>
				</arg-value>
			</do-set-dest-attr-value>
			<do-break/>
		</actions>
	</rule>
	<rule>
		<description>3. Set local variables</description>
		<conditions>
			<and>
				<if-class-name op="equal">User</if-class-name>
				<if-op-attr name="Group Membership" op="changing"/>
			</and>
		</conditions>
		<actions>
			<do-set-local-variable name="addmember">
				<arg-string>
					<token-xpath expression="modify-attr[@attr-name='Group Membership']/add-value/value[1]"/>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="delmember">
				<arg-string>
					<token-xpath expression="modify-attr[@attr-name='Group Membership']/remove-value/value[1]"/>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="account-is-disabled">
				<arg-string>
					<token-src-attr name="Login Disabled"/>
				</arg-string>
			</do-set-local-variable>
		</actions>
	</rule>
	<rule>
		<description>4. Modify: Check if app1 group is being added</description>
		<conditions>
			<and>
				<if-class-name op="equal">User</if-class-name>
				<if-op-attr name="Group Membership" op="changing"/>
				<if-local-variable mode="regex" name="addmember" op="equal">.Company001.XX.Groups.app1.app-.*</if-local-variable>
				<if-local-variable name="account-is-disabled" op="not-equal">true</if-local-variable>
			</and>
		</conditions>
		<actions>
			<do-set-local-variable name="profile-name">
				<arg-string>
					<token-substring length="64" start="32">
						<token-local-variable name="addmember"/>
					</token-substring>
				</arg-string>
			</do-set-local-variable>
			<do-add-dest-attr-value name="Company:app1">
				<arg-value type="string">
					<token-local-variable name="profile-name"/>
				</arg-value>
			</do-add-dest-attr-value>
			<do-break/>
		</actions>
	</rule>
	<rule>
		<description>5. Modify: Check if app1 group is being deleted</description>
		<conditions>
			<and>
				<if-class-name op="equal">User</if-class-name>
				<if-op-attr name="Group Membership" op="changing"/>
				<if-local-variable mode="regex" name="delmember" op="equal">.Company001.XX.Groups.app1.app-.*</if-local-variable>
			</and>
		</conditions>
		<actions>
			<do-clear-dest-attr-value name="Company:app1"/>
			<do-break/>
		</actions>
	</rule>
	<rule>
		<description>6. Modify: Check if app2 group is being added</description>
		<conditions>
			<and>
				<if-class-name op="equal">User</if-class-name>
				<if-op-attr name="Group Membership" op="changing"/>
				<if-local-variable mode="regex" name="addmember" op="equal">.Company001.XX.Groups.app2.app-app2</if-local-variable>
				<if-local-variable name="account-is-disabled" op="not-equal">true</if-local-variable>
			</and>
		</conditions>
		<actions>
			<do-set-dest-attr-value name="Company:app2">
				<arg-value type="int">
					<token-text xml:space="preserve">1</token-text>
				</arg-value>
			</do-set-dest-attr-value>
			<do-break/>
		</actions>
	</rule>
	<rule>
		<description>7. Modify: Check if app2 group is being deleted</description>
		<conditions>
			<and>
				<if-class-name op="equal">User</if-class-name>
				<if-op-attr name="Group Membership" op="changing"/>
				<if-local-variable mode="regex" name="delmember" op="equal">.Company001.XX.Groups.app2.app-app2</if-local-variable>
			</and>
		</conditions>
		<actions>
			<do-clear-dest-attr-value name="Company:app2"/>
		</actions>
	</rule>
</policy>


Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

© 2014 Novell