Carnegie Learning LdapOrm
A Symfony bundle that provides ORM over LDAP., (*1)
This code is based upon Ucsf Ldap Orm which in turn is based on Mathieu Goulin's GorgLdapOrmBundle., (*2)
We are forever indebted to them for providing an excellent base for the work we've continued at Carnegie Learning. Originally we forked UcsfLdapOrm, however we found many improvements and wanted to give back to the community., (*3)
What's changed and/or been added so far:, (*4)
- Added the
LdapEntity
class. This is a Symfony entity which represents the top
LDAP object class.
- Added many subclasses of
LdapEntity
to describe the object classes from top
down to InetOrgPerson
.
- Added
Repository::filterByComplex()
which gives the entity manager/repository the ability to filter with custom constructed, complex boolean logic. (See code comment API documentation for details.)
- Removed the dependency upon TWIG at all.
Installation
Requires PHP5.5+ and Symphony 2.8+, (*5)
- Add with composer
composer require carnegielearning/ldap-orm-bundle
- Add the bundle to AppKernel.php
new CarnegieLearning\LdapOrmBundle\CarnegieLearningLdapOrmBundle()
- Install using composer
$ composer update carnegielearning/ldap-orm-bundle
Testing Requirements
It is suggested that you run the unit tests using grunt. This will allow for an in memory ldap server to be stood up in place and facilitate file watching as well., (*6)
- Install Node.js and npm
- Install Grunt into the global namespace. Refer to the grunt documentation for help.
- Run the installation for npm.
- Be sure that a java binary exists within the development environment. Please refer to the ldap sdk form more information.
- Run grunt. This will run a file watcher that watches for changes in the source folders/files as well as test folders/files. To exit be sure to use ctrl-c as this will kill the in-memory-ldap server
- It is possible to also run just the unit tests with out the file watcher portion. This can be accomplished with
Documentation
Develop with LdapOrm
some_ldap_server:
connection:
uri: ldaps://ldap.example.com
use_tls: true
bind_dn: cn=admin,dc=example,dc=com
password: shhhItsASecret
password_type: plaintext
-
uri: The URI you need for connecting to the LDAP service.
-
use_tls: 'true' or 'false' to decide on connecting with TLS
-
bind_dn: The DN for binding to the LDAP service
-
password: The password associated with the given bind DN
-
password_type:
sha1
or plaintext
. I use plaintext when the URI is LDAPS.
Dependency injection for LDAP Entity Managers and Services
services:
myldap_entity_manager:
class: Ucsf\LdapOrmBundle\Ldap\LdapEntityManager
arguments: ["@logger", "@annotation_reader", "%some_ldap_server%"]
comexample_person_service:
class: MyBundle\ComExamplePersonService
arguments: [ @myldap_entity_manager ]
Creating Entities (usually to represent an object class)
/**
* Represents a ComExamplePerson object class, which is a subclass of InetOrgPerson
*
* @ObjectClass("comExamplePerson")
* @SearchDn("ou=people,dc=example,dc=come")
* @Dn("uid={{ entity.uid }},ou=people,dc=example,dc=com")
*/
class ComExamplePerson extends InetOrgPerson
{
/**
* @Attribute("comExampleFavoriteIceCreamFlavor")
* @Must()
* @ArrayField()
*
* The @Attribute annotation relates the $comExampleFavoriteIceCreamFlavor member variable to the
* 'comExampleFavoriteIceCreamFlavor' attribute within the ComExamplePerson object class in LDAP.
* You don't have to name the PHP variable the same as your attribute name, but it helps to be
* consistent in this way.
*
* The @Must annotation requires this attribute to not be empty when persisting back to LDAP. If
* a @Must requirement is not satisfied, attempting to persist the entry will throw
* a MissingMustAttributeException.
*
* The @ArrayField aannotation tells the LDAP Entity Manager, repositories and services to treat
* this attribute as a multi-value LDAP field. This is unfortunately backwards from LDAP's default
* to multi-value an attribute. Baring miracles (i.e. finding the time), this will probably not be "fixed".
*
*/
protected $comExampleFavoriteIceCreamFlavor;
...
public function getComExampleFavoriteIceCreamFlavor() {
return $this->comExampleFavoriteIceCreamFlavor;
}
public function setComExampleFavoriteIceCreamFlavor($comExampleFavoriteIceCreamFlavor) {
$this->comExampleFavoriteIceCreamFlavor = $comExampleFavoriteIceCreamFlavor;
}
...
}
#### Coding the Service
class ComExamplePersonService {, (*7)
protected $comExamplePersonRepository;
public function __construct(LdapEntityManager $entityManager) {
// Make a repo for ComExamplePerson entities
$this->comExamplePersonRepository = $entityManager->getRepository(ComExamplePerson::class);
// Make a another repo for SomethignElse entities (just another example...)
$this->somethingElseRepository = $entityManager->getRepository(SomethingElse::class);
...
}
public function getPersonByUid($uid, $includeAddress = false, $attributes = null) {
$person = $this->comExampePersonRepository->findByUid($uid, $attributes);
...
return $person;
}
```, (*8)
A Controller... to Round it Out
class PeopleController extends Controller {
/**
* @Route("/person/detail/{uid}")
* @Template()
*/
public function detailAction(Request $request, $uid)
{
$comExamplePersonService = $this->get('comexample_person_service');
$person = $comExamplePersonService->getPersonByUid($uid);
...
return array('person' => $person);
}
To do
- ~~Remove need for generic LDAP config~~
- ~~Configuration documentation~~
- ~~Development example~~
- Rewrite test suite (In progress...)
- Remove deprecated search results iterator