[Date Prev][Date Next] [Chronological] [Thread] [Top]

modrelay 0.1



This is an OpenLDAP overlay that intercepts ADD and MODIFY requests
for posixAccount and organization type entries, and relays the changes to a postgresql database.


This code CANNOT be used anywhere else without modification. Consider it sample code only! Posting only in the spirit of share and share alike.

Version 0.2 of the attached code will handle modrdn and delete requests and read the connection string from slapd.conf. This version is simpler.

jr
/**
 * modrelay.c 
 *
 * Copyright (C) 2008 Joel W. Reed
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * http://www.OpenLDAP.org/license.html.
 *
 * SEE LICENSE FOR MORE INFORMATION
 *
 * Author:	Joel W. Reed
 * Email:	joelwreed@gmail.com
 * Version: 0.1
 * Updated: 06.29.2008
 * 
 * modrelay
 *
 * This is an OpenLDAP overlay that intercepts ADD and MODIFY requests 
 * for posixAccount and organization type entries, and relays the changes to 
 * a postgresql database.
 */

#include "portable.h" 
#include <ac/string.h>
#include <ac/ctype.h>
#include "slap.h"
#include "ldif.h"
#include "libpq-fe.h"
#include "config.h" 

// TODO: figure out config api
#define CONNECTION_STRING "your connection string goes here"

static slap_overinst modrelay;
static ObjectClass *oc_posix_account;
static ObjectClass *oc_organization;
static ObjectClass *oc_dcobject;

typedef struct modrelay_data {
	ldap_pvt_thread_mutex_t mutex;
	PGconn* connection;
} modrelay_data;

PGconn* get_db_connection(modrelay_data *ad)
{
  // we lazily connect to the db whenever first needed
  if (ad->connection != NULL) return ad->connection;

	ad->connection = PQconnectdb(CONNECTION_STRING); 
	if (!ad->connection || (PQstatus(ad->connection) != CONNECTION_OK))
		{
			Debug( LDAP_DEBUG_ANY, "%s: failed to connect to database!\n",
						 modrelay.on_bi.bi_type, 0, 0 );
		}
  
  return ad->connection;
}

static void modrelay_process_organization(Operation* op, Entry* entry)
{
	static const char INSERT_SQL[] = "insert into organization (id, version, name, description, mail_host, dn) values (nextval('organization_sequence'), 0, $1, $2, $3, $4)";
	static const char UPDATE_SQL[] = "update organization set name=$1, description=$2, mail_host=$3 where dn=$4";
	static int PARAM_CNT = 4;

	slap_overinst* on = (slap_overinst *)op->o_bd->bd_info;
	modrelay_data *ad = on->on_bi.bi_private;
	PGconn* connection = get_db_connection(ad);

	const char* paramValues[PARAM_CNT];
	paramValues[0] = paramValues[1] = paramValues[2] = "";
	paramValues[3] = entry->e_nname.bv_val;

	Debug( LDAP_DEBUG_TRACE, "%s: modrelay_response for add/edit organization %s\n",
				 modrelay.on_bi.bi_type, paramValues[3], 0 );

	Attribute* attr;
	for (attr = entry->e_attrs; attr; attr = attr->a_next )
		{
			if (!strcmp(attr->a_desc->ad_cname.bv_val, "o"))
				paramValues[0] = attr->a_vals[0].bv_val;
			else if (!strcmp(attr->a_desc->ad_cname.bv_val, "description"))
				paramValues[1] = attr->a_vals[0].bv_val;
			else if (!strcmp(attr->a_desc->ad_cname.bv_val, "mailHost"))
				paramValues[2] = attr->a_vals[0].bv_val;
		}

	const char* SQL = (op->o_tag == LDAP_REQ_ADD)? INSERT_SQL : UPDATE_SQL;
	PGresult* res = PQexecParams(connection, SQL, PARAM_CNT, NULL, paramValues,
															 NULL, NULL, 0);

	ExecStatusType status = PQresultStatus(res);
	if (status != PGRES_COMMAND_OK)
		{
			Debug(LDAP_DEBUG_ANY, "%s: org database error: (%d) %s\n",
						modrelay.on_bi.bi_type, status, PQresultErrorMessage(res));
			Debug(LDAP_DEBUG_ANY, "%s: SQL: %s o:%s\n",
						modrelay.on_bi.bi_type, SQL, paramValues[0]);
			Debug(LDAP_DEBUG_ANY, "%s: desc:%s, mailHost:%s\n",
						modrelay.on_bi.bi_type, paramValues[1], paramValues[2]);
		}

	PQclear(res);
}

static void modrelay_process_user(Operation* op, Entry* entry)
{
	slap_overinst* on = (slap_overinst *)op->o_bd->bd_info;
	modrelay_data *ad = on->on_bi.bi_private;
	PGconn* connection = get_db_connection(ad);

	static const char INSERT_SQL[] = "insert into person (id, version, first_name, last_name, email_address, dn) values (nextval('person_sequence'), 0, $1, $2, $3, $4)";
	static const char UPDATE_SQL[] = "update person set first_name=$1, last_name=$2, email_address=$3 where dn=$4";
	static int PARAM_CNT = 4;

	const char* paramValues[PARAM_CNT];
	paramValues[0] = paramValues[1] = paramValues[2] = "";
	paramValues[3] = entry->e_nname.bv_val;

	Debug( LDAP_DEBUG_TRACE, "%s: modrelay_response for add/edit user \n",
				 modrelay.on_bi.bi_type, 0, 0 );

	Attribute* attr;
	for (attr = entry->e_attrs; attr; attr = attr->a_next )
		{
			if (!strcmp(attr->a_desc->ad_cname.bv_val, "givenName"))
				paramValues[0] = attr->a_vals[0].bv_val;
			else if (!strcmp(attr->a_desc->ad_cname.bv_val, "sn"))
				paramValues[1] = attr->a_vals[0].bv_val;
			else if (!strcmp(attr->a_desc->ad_cname.bv_val, "mail"))
				paramValues[2] = attr->a_vals[0].bv_val;
		}

	const char* SQL = (op->o_tag == LDAP_REQ_ADD)? INSERT_SQL : UPDATE_SQL;
	PGresult* res = PQexecParams(connection, SQL, PARAM_CNT, NULL, paramValues,
															 NULL, NULL, 0);

	ExecStatusType status = PQresultStatus(res);
	if (status != PGRES_COMMAND_OK)
		{
			Debug(LDAP_DEBUG_ANY, "%s: user database error: (%d) %s\n",
						modrelay.on_bi.bi_type, status, PQresultErrorMessage(res));
		}

	PQclear(res);
	
	if (op->o_tag == LDAP_REQ_ADD)
		{
			struct berval usersdn, orgdn;
			dnParent(&entry->e_nname, &usersdn ); 
			dnParent(&usersdn, &orgdn); 

			Debug( LDAP_DEBUG_TRACE, "%s: modrelay_response linking user: %s to org: %s \n",
						 modrelay.on_bi.bi_type, entry->e_nname.bv_val, orgdn.bv_val );

			static const char LINK_SQL[] = "insert into organization_person (organization_person_id, person_id) values ((select id from organization where dn=$1), (select id from person where dn=$2))";
			paramValues[0] = orgdn.bv_val;
			paramValues[1] = entry->e_nname.bv_val;
			
			res = PQexecParams(connection, LINK_SQL, 2, NULL, paramValues,
                         NULL, NULL, 0);

			status = PQresultStatus(res);
			if (status != PGRES_COMMAND_OK)
				{
					Debug(LDAP_DEBUG_ANY, "%s: user link database error: (%d) %s\n",
								modrelay.on_bi.bi_type, status, PQresultErrorMessage(res));
				}

			PQclear(res);
		}
}

static int modrelay_response( Operation *op, SlapReply *rs )
{
	slap_overinst* on = (slap_overinst *)op->o_bd->bd_info;
	modrelay_data *ad = on->on_bi.bi_private;
	Entry* entry = NULL;
		
	/* if error no point in continuing */
	if ( rs->sr_err != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;

	switch (op->o_tag)
		{
		case LDAP_REQ_ADD:
			entry = op->ora_e;
			break;
		case LDAP_REQ_MODIFY:	
			if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &entry, on ) != LDAP_SUCCESS || entry == NULL ) {
				Debug( LDAP_DEBUG_ANY, "modrelay_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
				return SLAP_CB_CONTINUE;
			}
			break;
		default:
			return SLAP_CB_CONTINUE;
		}

	ldap_pvt_thread_mutex_lock(&ad->mutex);

	/* is this a object class we care about */
	if (is_entry_objectclass( (entry), oc_posix_account, 0))
		modrelay_process_user(op, entry);
	else if ((is_entry_objectclass( (entry), oc_organization, 0))&&
					 (is_entry_objectclass( (entry), oc_dcobject, 0)))
		modrelay_process_organization(op, entry);

	ldap_pvt_thread_mutex_unlock(&ad->mutex);
 
	if (op->o_tag == LDAP_REQ_MODIFY)
		overlay_entry_release_ov( op, entry, 0, on );
		
	return SLAP_CB_CONTINUE;
}

static int modrelay_delete(Operation *op, SlapReply *rs )
{
	return SLAP_CB_CONTINUE;
}

static int modrelay_db_init(BackendDB *be,	ConfigReply *cr)
{
	slap_overinst *on = (slap_overinst *)be->bd_info;
	modrelay_data *ad = ch_calloc(1, sizeof(modrelay_data));

	on->on_bi.bi_private = ad;
	ldap_pvt_thread_mutex_init( &ad->mutex );
	ad->connection = NULL;

	return 0;
}

static int modrelay_db_destroy(BackendDB *be, ConfigReply *cr)
{
	slap_overinst *on = (slap_overinst *)be->bd_info;
	modrelay_data *ad = on->on_bi.bi_private;

	ldap_pvt_thread_mutex_destroy( &ad->mutex );
	if (ad->connection != NULL) PQfinish(ad->connection);

	free( ad );
	return 0;
}

int modrelay_init() 
{
	modrelay.on_bi.bi_type = "modrelay";
	modrelay.on_bi.bi_db_init = modrelay_db_init;
	modrelay.on_bi.bi_db_destroy = modrelay_db_destroy;
	modrelay.on_response = modrelay_response;
	modrelay.on_bi.bi_op_delete = modrelay_delete;

	oc_posix_account = oc_find( "posixAccount" );
	if(oc_posix_account == NULL) {
		Debug( LDAP_DEBUG_ANY, "%s: unable to find default ObjectClass \"posixAccount\".\n",
					 modrelay.on_bi.bi_type, 0, 0 );
		return -1;
	}

	oc_organization = oc_find( "organization" );
	if(oc_organization == NULL) {
		Debug( LDAP_DEBUG_ANY, "%s: unable to find default ObjectClass \"organization\".\n",
					 modrelay.on_bi.bi_type, 0, 0 );
		return -1;
	}

	oc_dcobject = oc_find( "dcObject" );
	if(oc_dcobject == NULL) {
		Debug( LDAP_DEBUG_ANY, "%s: unable to find default ObjectClass \"dcObject\".\n",
					 modrelay.on_bi.bi_type, 0, 0 );
		return -1;
	}

	return ( overlay_register(&modrelay) );
}

int init_module( int argc, char *argv[] ) 
{
	return modrelay_init();
}