Skip to Content.
Sympa Menu

comanage-dev - [comanage-dev] r578 - in registry/trunk/app: Config/Schema Controller Lib Model Model/Behavior Plugin/ChangelogProvisioner/Config/Schema Plugin/ChangelogProvisioner/Model Plugin/LdapProvisioner/Config/Schema Plugin/LdapProvisioner/Controller Plugin/LdapProvisioner/Lib Plugin/LdapProvisioner/Model Plugin/LdapProvisioner/View/CoLdapProvisionerTargets View/CoGroups View/CoPeople View/Layouts

Subject: COmanage Developers List

List archive

[comanage-dev] r578 - in registry/trunk/app: Config/Schema Controller Lib Model Model/Behavior Plugin/ChangelogProvisioner/Config/Schema Plugin/ChangelogProvisioner/Model Plugin/LdapProvisioner/Config/Schema Plugin/LdapProvisioner/Controller Plugin/LdapProvisioner/Lib Plugin/LdapProvisioner/Model Plugin/LdapProvisioner/View/CoLdapProvisionerTargets View/CoGroups View/CoPeople View/Layouts


Chronological Thread 
  • From:
  • To:
  • Subject: [comanage-dev] r578 - in registry/trunk/app: Config/Schema Controller Lib Model Model/Behavior Plugin/ChangelogProvisioner/Config/Schema Plugin/ChangelogProvisioner/Model Plugin/LdapProvisioner/Config/Schema Plugin/LdapProvisioner/Controller Plugin/LdapProvisioner/Lib Plugin/LdapProvisioner/Model Plugin/LdapProvisioner/View/CoLdapProvisionerTargets View/CoGroups View/CoPeople View/Layouts
  • Date: Sun, 25 Aug 2013 19:41:19 -0400

Author: benno
Date: 2013-08-25 19:41:19 -0400 (Sun, 25 Aug 2013)
New Revision: 578

Added:
registry/trunk/app/Model/CoProvisioningExport.php
registry/trunk/app/View/CoGroups/provision.ctp
Removed:

registry/trunk/app/Plugin/ChangelogProvisioner/Model/CoChangelogProvisionerExport.php
Modified:
registry/trunk/app/Config/Schema/schema.xml
registry/trunk/app/Controller/AppController.php
registry/trunk/app/Controller/CoGroupsController.php
registry/trunk/app/Controller/CoProvisioningTargetsController.php
registry/trunk/app/Lib/enum.php
registry/trunk/app/Lib/lang.php
registry/trunk/app/Model/Behavior/ProvisionerBehavior.php
registry/trunk/app/Model/CoGroup.php
registry/trunk/app/Model/CoGroupMember.php
registry/trunk/app/Model/CoPerson.php
registry/trunk/app/Model/CoProvisionerPluginTarget.php
registry/trunk/app/Model/CoProvisioningTarget.php
registry/trunk/app/Plugin/ChangelogProvisioner/Config/Schema/schema.xml

registry/trunk/app/Plugin/ChangelogProvisioner/Model/ChangelogProvisioner.php

registry/trunk/app/Plugin/ChangelogProvisioner/Model/CoChangelogProvisionerTarget.php
registry/trunk/app/Plugin/LdapProvisioner/Config/Schema/schema.xml

registry/trunk/app/Plugin/LdapProvisioner/Controller/CoLdapProvisionerTargetsController.php
registry/trunk/app/Plugin/LdapProvisioner/Lib/lang.php
registry/trunk/app/Plugin/LdapProvisioner/Model/CoLdapProvisionerDn.php
registry/trunk/app/Plugin/LdapProvisioner/Model/CoLdapProvisionerTarget.php
registry/trunk/app/Plugin/LdapProvisioner/Model/LdapProvisioner.php

registry/trunk/app/Plugin/LdapProvisioner/View/CoLdapProvisionerTargets/fields.inc
registry/trunk/app/View/CoGroups/fields.inc
registry/trunk/app/View/CoPeople/provision.ctp
registry/trunk/app/View/Layouts/default.ctp
Log:
Provision groups and related cleanup (CO-664,CO-642,CO-663,CO-665,CO-670)

Modified: registry/trunk/app/Config/Schema/schema.xml
===================================================================
--- registry/trunk/app/Config/Schema/schema.xml 2013-08-21 20:36:01 UTC (rev
577)
+++ registry/trunk/app/Config/Schema/schema.xml 2013-08-25 23:41:19 UTC (rev
578)
@@ -485,6 +485,7 @@
</field>
<field name="attrs_from_ldap" type="L" />
<field name="attrs_from_saml" type="L" />
+ <field name="attrs_from_env" type="L" />
<field name="attrs_from_coef" type="L" />
<field name="pool_org_identities" type="L" />
<field name="status" type="C" size="2" />
@@ -506,6 +507,7 @@
</field>
<field name="type" type="C" size="16" />
<field name="required" type="I" />
+ <field name="env_name" type="C" size="80" />
<field name="ldap_name" type="C" size="80" />
<field name="saml_name" type="C" size="80" />
<field name="created" type="T" />
@@ -830,6 +832,37 @@
</index>
</table>

+ <table name="co_provisioning_exports">
+ <field name="id" type="I">
+ <key />
+ <autoincrement />
+ </field>
+ <field name="co_provisioning_target_id" type="I">
+ <constraint>REFERENCES cm_co_provisioning_targets(id)</constraint>
+ </field>
+ <field name="co_person_id" type="I">
+ <constraint>REFERENCES cm_co_people(id)</constraint>
+ </field>
+ <field name="co_group_id" type="I">
+ <constraint>REFERENCES cm_co_groups(id)</constraint>
+ </field>
+ <field name="exporttime" type="T" />
+ <field name="created" type="T" />
+ <field name="modified" type="T" />
+
+ <index name="co_provisioning_exports_i1">
+ <col>co_provisioning_target_id</col>
+ <col>co_person_id</col>
+ <unique />
+ </index>
+
+ <index name="co_provisioning_exports_i2">
+ <col>co_provisioning_target_id</col>
+ <col>co_group_id</col>
+ <unique />
+ </index>
+ </table>
+
<table name="co_provisioning_queued_events">
<field name="id" type="I">
<key />
@@ -866,6 +899,8 @@
<field name="url" type="C" size="256" />
<field name="ordr" type="I" />
<field name="location" type="C" size="32" />
+
+ <!-- XXX almost certainly need to create some indexes here -->
</table>

<table name="navigation_links">
@@ -880,5 +915,7 @@
<field name="url" type="C" size="256" />
<field name="ordr" type="I" />
<field name="location" type="C" size="32" />
+
+ <!-- XXX almost certainly need to create some indexes here -->
</table>
</schema>

Modified: registry/trunk/app/Controller/AppController.php
===================================================================
--- registry/trunk/app/Controller/AppController.php 2013-08-21 20:36:01
UTC (rev 577)
+++ registry/trunk/app/Controller/AppController.php 2013-08-25 23:41:19
UTC (rev 578)
@@ -1000,7 +1000,6 @@
*/

function getNavLinks() {
-
// Get CMP-level navigation links
$this->loadModel('NavigationLink');


Modified: registry/trunk/app/Controller/CoGroupsController.php
===================================================================
--- registry/trunk/app/Controller/CoGroupsController.php 2013-08-21
20:36:01 UTC (rev 577)
+++ registry/trunk/app/Controller/CoGroupsController.php 2013-08-25
23:41:19 UTC (rev 578)
@@ -343,6 +343,9 @@
}
}

+ // (Re)provision an existing CO Group?
+ $p['provision'] = ($roles['cmadmin'] || $roles['coadmin'] ||
$roles['couadmin']);
+
// Select from a list of potential Groups to join?
$p['select'] = ($roles['cmadmin']
|| ($managedp && $roles['coadmin'])
@@ -378,6 +381,26 @@
}

/**
+ * Obtain provisioning status for CO Group
+ *
+ * @param integer CO Group ID
+ * @since COmanage Registry v0.8.2
+ */
+
+ function provision($id) {
+ if(!$this->restful) {
+ // Pull some data for the view to be able to render
+ $this->set('co_provisioning_status',
$this->CoGroup->provisioningStatus($id));
+
+ $args = array();
+ $args['conditions']['CoGroup.id'] = $id;
+ $args['contain'] = false;
+
+ $this->set('co_group', $this->CoGroup->find('first', $args));
+ }
+ }
+
+ /**
* Obtain groups available for a CO Person to join.
* - precondition: $this->request->params holds copersonid XXX we don't do
anything with this yet
* - postcondition: $co_groups set (HTML)

Modified: registry/trunk/app/Controller/CoProvisioningTargetsController.php
===================================================================
--- registry/trunk/app/Controller/CoProvisioningTargetsController.php
2013-08-21 20:36:01 UTC (rev 577)
+++ registry/trunk/app/Controller/CoProvisioningTargetsController.php
2013-08-25 23:41:19 UTC (rev 578)
@@ -107,22 +107,28 @@
*/

function checkWriteFollowups($reqdata, $curdata = null) {
- // Create an instance of the plugin provisioning target. We do this here
to avoid
- // an inconsistent state where the co_provisioning_target is created
without a
- // corresponding plugin record.
+ if(!$curdata) {
+ // Create an instance of the plugin provisioning target. We do this
here to avoid
+ // an inconsistent state where the co_provisioning_target is created
without a
+ // corresponding plugin record.
+
+ // A better check would be to see if there is an existing
corresponding row
+ // (rather than !$curdata) since we don't fail if the initial attempt
to create
+ // the row fails.
+
+ $pluginName = $reqdata['CoProvisioningTarget']['plugin'];
+ $modelName = 'Co'. $pluginName . 'Target';
+ $pluginModelName = $pluginName . "." . $modelName;
+
+ $target = array();
+ $target[$modelName]['co_provisioning_target_id'] =
$this->CoProvisioningTarget->id;
+
+ // Note that we have to disable validation because we want to create
an empty row.
+ $this->loadModel($pluginModelName);
+ $this->$modelName->save($target, false);
+ $this->_targetid = $this->$modelName->id;
+ }

- $pluginName = $reqdata['CoProvisioningTarget']['plugin'];
- $modelName = 'Co'. $pluginName . 'Target';
- $pluginModelName = $pluginName . "." . $modelName;
-
- $target = array();
- $target[$modelName]['co_provisioning_target_id'] =
$this->CoProvisioningTarget->id;
-
- // Note that we have to disable validation because we want to create an
empty row.
- $this->loadModel($pluginModelName);
- $this->$modelName->save($target, false);
- $this->_targetid = $this->$modelName->id;
-
return true;
}

@@ -206,8 +212,8 @@
}

/**
- * Execute (re)provisioning for the specified CO Person.
- * - precondition: CO Person ID passed via named parameter
+ * Execute (re)provisioning for the specified CO Person or CO Group.
+ * - precondition: CO Person ID or CO Group ID passed via named parameter
* - postcondition: Provisioning queued or executed
*
* @param integer CO Provisioning Target ID
@@ -216,49 +222,71 @@

function provision($id) {
if($this->restful) {
+ $copersonid = null;
+ $cogroupid = null;
+
if(!empty($this->request->params['named']['copersonid'])) {
- // Make sure copersonid is in the same CO as $id
-
- $args = array();
+ $copersonid = $this->request->params['named']['copersonid'];
+ } elseif(!empty($this->request->params['named']['cogroupid'])) {
+ $cogroupid = $this->request->params['named']['cogroupid'];
+ } else {
+ $this->restResultHeader(500, "Bad Request");
+ }
+
+ // Make sure copersonid or cogroupid is in the same CO as $id
+
+ $args = array();
+ if($copersonid) {
$args['joins'][0]['table'] = 'co_people';
$args['joins'][0]['alias'] = 'CoPerson';
$args['joins'][0]['type'] = 'INNER';
$args['joins'][0]['conditions'][0] =
'CoProvisioningTarget.co_id=CoPerson.co_id';
$args['conditions']['CoProvisioningTarget.id'] = $id;
- $args['conditions']['CoPerson.id'] =
$this->request->params['named']['copersonid'];
+ $args['conditions']['CoPerson.id'] = $copersonid;
$args['contain'] = false;
-
- if($this->CoProvisioningTarget->find('count', $args) < 1) {
- $this->restResultHeader(404, "CoPerson Not Found");
- return;
+ } else {
+ $args['joins'][0]['table'] = 'co_groups';
+ $args['joins'][0]['alias'] = 'CoGroup';
+ $args['joins'][0]['type'] = 'INNER';
+ $args['joins'][0]['conditions'][0] =
'CoProvisioningTarget.co_id=CoGroup.co_id';
+ $args['conditions']['CoProvisioningTarget.id'] = $id;
+ $args['conditions']['CoGroup.id'] = $cogroupid;
+ $args['contain'] = false;
+ }
+
+ if($this->CoProvisioningTarget->find('count', $args) < 1) {
+ // XXX this could also be co provisioning target not found -- do a
separate find to check?
+ $this->restResultHeader(404, $args['joins'][0]['alias'] . " Not
Found");
+ return;
+ }
+
+ // Attach ProvisionerBehavior and manually invoke provisioning
+
+ try {
+ if($copersonid) {
+
$this->CoProvisioningTarget->Co->CoPerson->Behaviors->load('Provisioner');
+ $this->CoProvisioningTarget->Co->CoPerson->manualProvision($id,
$copersonid);
+ } else {
+
$this->CoProvisioningTarget->Co->CoGroup->Behaviors->load('Provisioner');
+ $this->CoProvisioningTarget->Co->CoGroup->manualProvision($id,
null, $cogroupid);
}
-
- // Attach ProvisionerBehavior and manually invoke provisioning
-
-
$this->CoProvisioningTarget->Co->CoPerson->Behaviors->load('Provisioner');
-
- try {
- $this->CoProvisioningTarget->Co->CoPerson->manualProvision($id,
$this->request->params['named']['copersonid']);
+ }
+ catch(InvalidArgumentException $e) {
+ switch($e->getMessage()) {
+ case _txt('er.cop.unk'):
+ $this->restResultHeader(404, $args['joins'][0]['alias'] . " Not
Found");
+ break;
+ case _txt('er.copt.unk'):
+ $this->restResultHeader(404, "CoProvisioningTarget Not Found");
+ break;
+ default:
+ $this->restResultHeader(500, $e->getMessage());
+ break;
}
- catch(InvalidArgumentException $e) {
- switch($e->getMessage()) {
- case _txt('er.cop.unk'):
- $this->restResultHeader(404, "CoPerson Not Found");
- break;
- case _txt('er.copt.unk'):
- $this->restResultHeader(404, "CoProvisioningTarget Not Found");
- break;
- default:
- $this->restResultHeader(500, $e->getMessage());
- break;
- }
- }
- catch(RuntimeException $e) {
- $this->restResultHeader(500, $e->getMessage());
- }
- } else {
- $this->restResultHeader(404, "CoPerson Not Found");
}
+ catch(RuntimeException $e) {
+ $this->restResultHeader(500, $e->getMessage());
+ }
}
}
}

Modified: registry/trunk/app/Lib/enum.php
===================================================================
--- registry/trunk/app/Lib/enum.php 2013-08-21 20:36:01 UTC (rev 577)
+++ registry/trunk/app/Lib/enum.php 2013-08-25 23:41:19 UTC (rev 578)
@@ -251,6 +251,10 @@
// The action for which a plugin may want to act on
class ProvisioningActionEnum
{
+ const CoGroupAdded = 'GA';
+ const CoGroupDeleted = 'GD';
+ const CoGroupReprovisionRequested = 'GR';
+ const CoGroupUpdated = 'GU';
const CoPersonAdded = 'PA';
const CoPersonDeleted = 'PD';
const CoPersonEnteredGracePeriod = 'PG';

Modified: registry/trunk/app/Lib/lang.php
===================================================================
--- registry/trunk/app/Lib/lang.php 2013-08-21 20:36:01 UTC (rev 577)
+++ registry/trunk/app/Lib/lang.php 2013-08-25 23:41:19 UTC (rev 578)
@@ -445,8 +445,10 @@
'fd.ef.cf.cmp' => 'Platform Enrollment Configuration',
'fd.ef.coef' => 'Enable Attributes Via CO Enrollment Flow',
'fd.ef.coef.desc' => 'If enabled, allow organizational identity attributes
to be collected via forms during CO enrollment flows (these attributes will
be less authoritative than those obtained via LDAP or SAML)',
- 'fd.ef.efn' => 'From Address For Notifications',
- 'fd.ef.efn.desc' => 'Email address notifications will come from',
+ 'fd.ef.efn' => 'From Address For Notifications',
+ 'fd.ef.efn.desc' => 'Email address notifications will come from',
+ 'fd.ef.env' => 'Enable Environment Attribute Retrieval',
+ 'fd.ef.env.desc' => 'Examine the server environment for authoritative
organizational identity attributes',
'fd.ef.epx' => 'Early Provisioning Executable',
'fd.ef.epx.desc' => '(Need for this TBD)',
'fd.ef.ldap' => 'Enable LDAP Attribute Retrieval',
@@ -613,7 +615,7 @@
'op.inv.reply' => 'Reply to Invitation',
'op.inv.resend' => 'Resend Invite',
'op.inv.send' => 'Send Invite',
- 'op.manage' => 'Manage',
+ 'op.manage.grm' => 'Manage Group Memberships',
'op.menu' => 'Menu',
'op.login' => 'Login',
'op.logout' => 'Logout',

Modified: registry/trunk/app/Model/Behavior/ProvisionerBehavior.php
===================================================================
--- registry/trunk/app/Model/Behavior/ProvisionerBehavior.php 2013-08-21
20:36:01 UTC (rev 577)
+++ registry/trunk/app/Model/Behavior/ProvisionerBehavior.php 2013-08-25
23:41:19 UTC (rev 578)
@@ -2,7 +2,7 @@
/**
* COmanage Registry Provisioner Behavior
*
- * Copyright (C) 2012 University Corporation for Advanced Internet
Development, Inc.
+ * Copyright (C) 2012-13 University Corporation for Advanced Internet
Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with
* the License. You may obtain a copy of the License at
@@ -14,7 +14,7 @@
* KIND, either express or implied. See the License for the specific
language governing
* permissions and limitations under the License.
*
- * @copyright Copyright (C) 2012 University Corporation for Advanced
Internet Development, Inc.
+ * @copyright Copyright (C) 2012-13 University Corporation for Advanced
Internet Development, Inc.
* @link http://www.internet2.edu/comanage COmanage Project
* @package registry
* @since COmanage Registry v0.8
@@ -41,9 +41,16 @@
public function afterDelete(Model $model) {
// Note that in most cases this is just an edit. ie: deleting a
telephone number is
// CoPersonUpdated not CoPersonDeleted. In those cases, we can just call
afterSave.
- // CoPerson was already handled by beforeDelete().
+ // CoPerson and CoGroup were already handled by beforeDelete().

- if($model->name != 'CoPerson') {
+ if($model->name != 'CoPerson' && $model->name != 'CoGroup') {
+ if($model->name == 'CoGroupMember') {
+ // For CoGroupMember, we need to restore the model data to have
access to
+ // the CoPerson and CoGroup we need to rewrite. (CO-663)
+
+ $model->data = $model->cacheData;
+ }
+
return $this->afterSave($model, false);
}

@@ -63,76 +70,147 @@
*/

public function afterSave(Model $model, $created) {
+ $pmodel = null;
+ $pdata = null;
+ $paction = null;
+
// For our initial implementation, one of the following must be true for
$model:
+ // - The model is CoGroup
// - The model is CoPerson
// - The model belongs to CoPerson, and co_person_id is set
// - The model belongs to CoPersonRole, and co_person_role_id is set
//
- // First, find the co_person_id (directly or indirectly) and pull the
record
+ // If the model is CoGroupMember, both CoGroup and CoPerson provisioning
are triggered.
+ // Note: this is likely triggering extra work, since we'll get a pattern
like (in LdapProvisioner)
+ // 1 - Delete Group
+ // 2 - Delete Group Member
+ // 3 - Rewrite Group
+ // 4 - Group does not exist, promote to Add
+ // 5 - Add fails because group has no members
+ // 6 - Rewrite Person
+ // We should be able to skip 3 - 5, but to do so we'd need to know we
were called
+ // because a Group was deleted and not because (say) a Person was
deleted, and at
+ // the moment we don't have a way to do that.

- $coPerson = null;
- $coPersonId = -1;
- $coPersonData = null;
-
- if($model->name == 'CoPerson'
- && !empty($model->data['CoPerson']['id'])) {
- $coPerson = $model;
- $coPersonId = $model->data['CoPerson']['id'];
- } elseif(!empty($model->data[ $model->name ]['co_person_id'])) {
- $coPerson = $model->CoPerson;
- $coPersonId = $model->data[ $model->name ]['co_person_id'];
- } elseif(!empty($model->data[ $model->name ]['co_person_role_id'])) {
- $coPerson = $model->CoPersonRole->CoPerson;
- $coPersonId = $model->CoPersonRole->field('co_person_id',
- array('id' => $model->data[
$model->name ]['co_person_role_id']));
- } else {
- // For the moment, we'll just return true here since we may be
processing
- // a multi-model transaction (eg: unlinking a dependency before
deleting a
- // parent model) or we may be saving OrgIdentity data.
+ if($model->name == 'CoGroup' || $model->name == 'CoGroupMember') {
+ // First, find the co_group_id and pull the record

- return true;
+ $coGroupId = -1;
+
+ if($model->name == 'CoGroup'
+ && !empty($model->data['CoGroup']['id'])) {
+ $pmodel = $model;
+ $coGroupId = $model->data['CoGroup']['id'];
+ } elseif(!empty($model->data[ $model->name ]['co_group_id'])) {
+ $pmodel = $model->CoGroup;
+ $coGroupId = $model->data[ $model->name ]['co_group_id'];
+ }
+
+ if($pmodel){
+ try {
+ $pdata = $this->marshallCoGroupData($pmodel, $coGroupId);
+ }
+ catch(InvalidArgumentException $e) {
+ throw new InvalidArgumentException($e->getMessage());
+ }
+
+ $paction = ProvisioningActionEnum::CoGroupUpdated;
+
+ // It's only an add operation if the model is CoPerson
+ if($created && $model->name == 'CoGroup') {
+ $paction = ProvisioningActionEnum::CoGroupAdded;
+ }
+
+ // Invoke all provisioning plugins
+
+ try {
+ $this->invokePlugins($pmodel,
+ $pdata,
+ $paction);
+ }
+ // What we really want to do here is catch the result (success or
exception)
+ // and set the appropriate session flash message, but we don't have
access to
+ // the current session, and anyway that doesn't cover RESTful
interactions.
+ // So instead we syslog (which is better than nothing).
+ catch(InvalidArgumentException $e) {
+ syslog(LOG_ERR, $e->getMessage());
+ //throw new InvalidArgumentException($e->getMessage());
+ }
+ catch(RuntimeException $e) {
+ syslog(LOG_ERR, $e->getMessage());
+ //throw new RuntimeException($e->getMessage());
+ }
+ } else {
+ // We're probably CoGroupMember being promoted to afterSave in the
middle of
+ // a CoGroup being deleted. In that scenario, we don't actually need
to try
+ // to re-provision the CoGroup, so just move on to the person.
+ }
}

- try {
- $coPersonData = $this->marshallCoPersonData($coPerson, $coPersonId);
+ if($model->name != 'CoGroup') {
+ // First, find the co_person_id (directly or indirectly) and pull the
record
+
+ $coPersonId = -1;
+
+ if($model->name == 'CoPerson'
+ && !empty($model->data['CoPerson']['id'])) {
+ $pmodel = $model;
+ $coPersonId = $model->data['CoPerson']['id'];
+ } elseif(!empty($model->data[ $model->name ]['co_person_id'])) {
+ $pmodel = $model->CoPerson;
+ $coPersonId = $model->data[ $model->name ]['co_person_id'];
+ } elseif(!empty($model->data[ $model->name ]['co_person_role_id'])) {
+ $pmodel = $model->CoPersonRole->CoPerson;
+ $coPersonId = $model->CoPersonRole->field('co_person_id',
+ array('id' =>
$model->data[ $model->name ]['co_person_role_id']));
+ } else {
+ // For the moment, we'll just return true here since we may be
processing
+ // a multi-model transaction (eg: unlinking a dependency before
deleting a
+ // parent model) or we may be saving OrgIdentity data.
+
+ return true;
+ }
+
+ try {
+ $pdata = $this->marshallCoPersonData($pmodel, $coPersonId);
+ }
+ catch(InvalidArgumentException $e) {
+ throw new InvalidArgumentException($e->getMessage());
+ }
+
+ // Determine the provisioning action
+
+ // For now, we don't support CoPersonEnteredGracePeriod,
CoPersonExpired,
+ // or CoPersonUnexpired.
+
+ $paction = ProvisioningActionEnum::CoPersonUpdated;
+
+ // It's only an add operation if the model is CoPerson
+ if($created && $model->name == 'CoPerson') {
+ $paction = ProvisioningActionEnum::CoPersonAdded;
+ }
+
+ // Invoke all provisioning plugins
+
+ try {
+ $this->invokePlugins($pmodel,
+ $pdata,
+ $paction);
+ }
+ // What we really want to do here is catch the result (success or
exception)
+ // and set the appropriate session flash message, but we don't have
access to
+ // the current session, and anyway that doesn't cover RESTful
interactions.
+ // So instead we syslog (which is better than nothing).
+ catch(InvalidArgumentException $e) {
+ syslog(LOG_ERR, $e->getMessage());
+ //throw new InvalidArgumentException($e->getMessage());
+ }
+ catch(RuntimeException $e) {
+ syslog(LOG_ERR, $e->getMessage());
+ //throw new RuntimeException($e->getMessage());
+ }
}
- catch(InvalidArgumentException $e) {
- throw new InvalidArgumentException($e->getMessage());
- }

- // Determine the provisioning action
-
- // For now, we don't support CoPersonEnteredGracePeriod, CoPersonExpired,
- // or CoPersonUnexpired.
-
- $action = ProvisioningActionEnum::CoPersonUpdated;
-
- // It's only an add operation if the model is CoPerson
- if($created && $model->name == 'CoPerson') {
- $action = ProvisioningActionEnum::CoPersonAdded;
- }
-
- // Invoke all provisioning plugins
-
- try {
- $this->invokePlugins($coPerson,
- $coPersonId,
- $coPersonData,
- $action);
- }
- // What we really want to do here is catch the result (success or
exception)
- // and set the appropriate session flash message, but we don't have
access to
- // the current session, and anyway that doesn't cover RESTful
interactions.
- // So instead we syslog (which is better than nothing).
- catch(InvalidArgumentException $e) {
- syslog(LOG_ERR, $e->getMessage());
- //throw new InvalidArgumentException($e->getMessage());
- }
- catch(RuntimeException $e) {
- syslog(LOG_ERR, $e->getMessage());
- //throw new RuntimeException($e->getMessage());
- }
-
return true;
}

@@ -149,22 +227,39 @@
// CoPersonUpdated not CoPersonDeleted. However, in those cases we don't
want to
// process anything until afterDelete().

- if($model->name != 'CoPerson') {
+ $mname = $model->name;
+
+ // We will generally cache the data prior to delete in case we want to do
+ // something interesting with it in afterDelete. As of this writing, the
only
+ // use case for this is when a CoGroupMember is removed, we need to know
which
+ // CoPerson and CoGroup to rewrite, and we have to do that in afterDelete
+ // (so the CoGroupMember doesn't show up anymore) (CO-663).
+
+ // Note that $model->data is generally populated by
StandardController::delete
+ // calling $model->read(), but for cascading deletes that function won't
be called.
+
+ if(empty($model->data)) {
+ $model->read();
+ }
+
+ $model->cacheData = $model->data;
+
+ if($mname != 'CoPerson' && $mname != 'CoGroup') {
return true;
}

- // However, deleting a CoPerson needs to be handled specially.
- // Note that $model->data is generally populated by
StandardController::delete
- // calling $model->read().
+ // However, deleting a CoPerson or CoGroup needs to be handled specially.

- if(!empty($model->data['CoPerson']['id'])) {
+
+ if(!empty($model->data[ $mname ]['id'])) {
// Invoke all provisioning plugins

try {
$this->invokePlugins($model,
- $model->data['CoPerson']['id'],
$model->data,
- ProvisioningActionEnum::CoPersonDeleted);
+ $mname == 'CoPerson'
+ ? ProvisioningActionEnum::CoPersonDeleted
+ : ProvisioningActionEnum::CoGroupDeleted);
}
// What we really want to do here is catch the result (success or
exception)
// and set the appropriate session flash message, but we don't have
access to
@@ -188,15 +283,14 @@
*
* @since COmanage Registry v0.8
* @param Array $coProvisioningTarget Array of CoProvisioningTarget data,
as returned by find()
- * @param integer $coPersonId CO Person to (re)provision
- * @param Array $coPersonData Data to pass to plugin, as returned by
marshallCoPersonData()
+ * @param Array $provisioningData Data to pass to plugin, as returned by
marshallCoPersonData() or marshallCoGroupData()
* @param ProvisioningActionEnum $action Action triggering provisioning
* @return boolean true on success, false on failure
* @throws InvalidArgumentException
* @throws RuntimeException
*/

- private function invokePlugin($coProvisioningTarget, $coPersonId,
$coPersonData, $action) {
+ private function invokePlugin($coProvisioningTarget, $provisioningData,
$action) {
if(!empty($coProvisioningTarget['plugin'])) {
$pluginName = $coProvisioningTarget['plugin'];
$modelName = 'Co'. $pluginName . 'Target';
@@ -217,19 +311,32 @@
try {
$pluginModel->provision($pluginTarget,
$action,
- $coPersonData);
+ $provisioningData);

- // It's a bit of a walk to get to HistoryRecord
-
$pluginModel->CoProvisioningTarget->Co->CoPerson->HistoryRecord->record(
- $coPersonData['CoPerson']['id'],
- null,
- null,
- CakeSession::read('Auth.User.co_person_id'),
- ($action == ProvisioningActionEnum::CoPersonReprovisionRequested
- ? ActionEnum::CoPersonManuallyProvisioned
- : ActionEnum::CoPersonProvisioned),
- _txt('rs.prov-a', array($coProvisioningTarget['description']))
+ // Create/update the export record
+
+ $pluginModel->CoProvisioningTarget->CoProvisioningExport->record(
+ $coProvisioningTarget['id'],
+ !empty($provisioningData['CoPerson']['id']) ?
$provisioningData['CoPerson']['id'] : null,
+ !empty($provisioningData['CoGroup']['id']) ?
$provisioningData['CoGroup']['id'] : null
);
+
+ // Cut a history record if we're provisioning a CO Person record.
+ // Currently, there is no equivalent concept for CO Groups.
+
+ if(!empty($provisioningData['CoPerson']['id'])) {
+ // It's a bit of a walk to get to HistoryRecord
+
$pluginModel->CoProvisioningTarget->Co->CoPerson->HistoryRecord->record(
+ $provisioningData['CoPerson']['id'],
+ null,
+ null,
+ CakeSession::read('Auth.User.co_person_id'),
+ ($action ==
ProvisioningActionEnum::CoPersonReprovisionRequested
+ ? ActionEnum::CoPersonManuallyProvisioned
+ : ActionEnum::CoPersonProvisioned),
+ _txt('rs.prov-a', array($coProvisioningTarget['description']))
+ );
+ }
}
catch(InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage());
@@ -251,27 +358,28 @@
* Invoke all provisioning plugins.
*
* @since COmanage Registry v0.8
- * @param Model $coPersonModel CoPerson Model
- * @param integer $coPersonId CO Person to (re)provision
- * @param Array $coPersonData Data to pass to plugin, as returned by
marshallCoPersonData()
+ * @param Model $model CoPerson or CoGroup Model
+ * @param Array $provisioningData Data to pass to plugins, as returned by
marshallCoPersonData() or marshallCoGroupData()
* @param ProvisioningActionEnum $action Action triggering provisioning
* @return boolean true on success
* @throws RuntimeException
*/

- private function invokePlugins($coPersonModel, $coPersonId, $coPersonData,
$action) {
+ private function invokePlugins($model, $provisioningData, $action) {
$err = "";

- // Pull the Provisioning Targets for this CO. We use the CO ID from
$coPersonData.
- // (Even if we wanted to pull it from the database via $coPersonId, we
can't
- // guarantee it'll be there -- eg after a delete of CO Person the link
will be gone.)
+ // Pull the Provisioning Targets for this CO. We use the CO ID from
$provisioningData.

$args = array();
$args['conditions']['CoProvisioningTarget.status'] =
ProvisionerStatusEnum::AutomaticMode;
- $args['conditions']['CoProvisioningTarget.co_id'] =
$coPersonData['CoPerson']['co_id'];
+ if(isset($provisioningData[ $model->name ]['co_id'])) {
+ $args['conditions']['CoProvisioningTarget.co_id'] = $provisioningData[
$model->name ]['co_id'];
+ } else {
+ throw new RuntimeException(_txt('er.co.specify'));
+ }
$args['contain'] = false;

- $targets = $coPersonModel->Co->CoProvisioningTarget->find('all', $args);
+ $targets = $model->Co->CoProvisioningTarget->find('all', $args);

if(!empty($targets)) {
foreach($targets as $target) {
@@ -279,8 +387,7 @@

try {
$this->invokePlugin($target['CoProvisioningTarget'],
- $coPersonId,
- $coPersonData,
+ $provisioningData,
$action);
}
catch(InvalidArgumentException $e) {
@@ -305,13 +412,14 @@
* @since COmanage Registry v0.8
* @param Model $model Model instance
* @param integer $coProvisioningTargetId CO Provisioning Target to
execute
- * @param integer $coPersonId CO Person to (re)provision
+ * @param integer $coPersonId CO Person to (re)provision (null if CO
Group ID set)
+ * @param integer $coPersonId CO Group to (re)provision (null if CO
Person ID set)
* @return boolean true on success, false on failure
* @throws InvalidArgumentException
* @throws RuntimeException
*/

- public function manualProvision(Model $model, $coProvisioningTargetId,
$coPersonId) {
+ public function manualProvision(Model $model, $coProvisioningTargetId,
$coPersonId, $coGroupId=null) {
// Find the associated Provisioning Target record

$args = array();
@@ -322,19 +430,27 @@
// containable behavior and make a second call for the plugin we want.
$args['contain'] = false;

- // Currently, CoPerson is the only model that calls manualProvision, so
we know
+ // Currently, CoPerson and CoGroup are the only model that calls
manualProvision, so we know
// how to find CoProvisioningTarget
$copt = $model->Co->CoProvisioningTarget->find('first', $args);

if(!empty($copt)) {
try {
- // Again, we're only called by CoPerson at the moment (so $model =
CoPerson)
- $coPersonData = $this->marshallCoPersonData($model, $coPersonId);
-
- $this->invokePlugin($copt['CoProvisioningTarget'],
- $coPersonId,
- $coPersonData,
-
ProvisioningActionEnum::CoPersonReprovisionRequested);
+ if($coPersonId) {
+ // $model = CoPerson
+ $provisioningData = $this->marshallCoPersonData($model,
$coPersonId);
+
+ $this->invokePlugin($copt['CoProvisioningTarget'],
+ $provisioningData,
+
ProvisioningActionEnum::CoPersonReprovisionRequested);
+ } else {
+ // $model = CoGroup
+ $provisioningData = $this->marshallCoGroupData($model, $coGroupId);
+
+ $this->invokePlugin($copt['CoProvisioningTarget'],
+ $provisioningData,
+
ProvisioningActionEnum::CoGroupReprovisionRequested);
+ }
}
catch(InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage());
@@ -350,6 +466,25 @@
}

/**
+ * Assemble CO Group Data to pass to provisioning plugin(s).
+ *
+ * @since COmanage Registry v0.8.2
+ * @param Model $coGroupModel CO Group Model instance
+ * @param integer $coGroupId CO Group to (re)provision
+ * @return Array Array of CO Group Data, as returned by find
+ * @throws InvalidArgumentException
+ */
+
+ private function marshallCoGroupData($coGroupModel, $coGroupId) {
+ $args = array();
+ $args['conditions']['CoGroup.id'] = $coGroupId;
+ // Only pull related models relevant for provisioning
+ $args['contain'][] = 'CoGroupMember';
+
+ return $coGroupModel->find('first', $args);
+ }
+
+ /**
* Assemble CO Person Data to pass to provisioning plugin(s).
*
* @since COmanage Registry v0.8

Modified: registry/trunk/app/Model/CoGroup.php
===================================================================
--- registry/trunk/app/Model/CoGroup.php 2013-08-21 20:36:01 UTC (rev
577)
+++ registry/trunk/app/Model/CoGroup.php 2013-08-25 23:41:19 UTC (rev
578)
@@ -36,7 +36,8 @@
"CoEnrollmentFlowAuthzCoGroup" => array(
'className' => 'CoEnrollmentFlow',
'foreignKey' => 'authz_co_group_id'
- )
+ ),
+ "CoProvisioningExport" => array('dependent' => true)
);

public $belongsTo = array("Co"); // A CoGroup is attached to one
CO
@@ -47,7 +48,7 @@
// Default ordering for find operations
public $order = array("CoGroup.name");

- public $actsAs = array('Containable');
+ public $actsAs = array('Containable', 'Provisioner');

// If true the data source for the model uses a relational database
// backend and if false then the data source is something else, perhaps
@@ -153,4 +154,54 @@

return $this->find('all', $args);
}
+
+ /**
+ * Determine the current status of the provisioning targets for this CO
Group.
+ *
+ * @since COmanage Registry v0.8.2
+ * @param Integer CO Group ID
+ * @return Array Current status of provisioning targets
+ * @throws RuntimeException
+ */
+
+ public function provisioningStatus($coGroupId) {
+ // First, obtain the list of active provisioning targets for this
group's CO.
+
+ $args = array();
+ $args['joins'][0]['table'] = 'co_groups';
+ $args['joins'][0]['alias'] = 'CoGroup';
+ $args['joins'][0]['type'] = 'INNER';
+ $args['joins'][0]['conditions'][0] =
'CoGroup.co_id=CoProvisioningTarget.co_id';
+ $args['conditions']['CoGroup.id'] = $coGroupId;
+ $args['conditions']['CoProvisioningTarget.status !='] =
ProvisionerStatusEnum::Disabled;
+ $args['contain'] = false;
+
+ $targets = $this->Co->CoProvisioningTarget->find('all', $args);
+
+ if(!empty($targets)) {
+ // Next, for each target ask the relevant plugin for the status for
this group.
+
+ // We may end up querying the same Plugin more than once, so maintain
a cache.
+ $plugins = array();
+
+ for($i = 0;$i < count($targets);$i++) {
+ $pluginModelName = $targets[$i]['CoProvisioningTarget']['plugin']
+ . ".Co" .
$targets[$i]['CoProvisioningTarget']['plugin'] . "Target";
+
+ if(!isset($plugins[ $pluginModelName ])) {
+ $plugins[ $pluginModelName ] =
ClassRegistry::init($pluginModelName, true);
+
+ if(!$plugins[ $pluginModelName ]) {
+ throw new RuntimeException(_txt('er.plugin.fail',
array($pluginModelName)));
+ }
+ }
+
+ $targets[$i]['status'] = $plugins[ $pluginModelName
]->status($targets[$i]['CoProvisioningTarget']['id'],
+ null,
+
$coGroupId);
+ }
+ }
+
+ return $targets;
+ }
}

Modified: registry/trunk/app/Model/CoGroupMember.php
===================================================================
--- registry/trunk/app/Model/CoGroupMember.php 2013-08-21 20:36:01 UTC (rev
577)
+++ registry/trunk/app/Model/CoGroupMember.php 2013-08-25 23:41:19 UTC (rev
578)
@@ -156,6 +156,43 @@
}

/**
+ * Map a set of CO Group Members to their Identifiers. Based on a similar
function in CoLdapProvisionerDn.php.
+ *
+ * @since COmanage Registry v0.8.2
+ * @param Array CO Group Members
+ * @param String Identifier to map to
+ * @param Boolean True to map owners, false to map members
+ * @return Array Array of Identifiers found -- note this array is not in
any particular order, and may have fewer entries
+ */
+
+ public function mapCoGroupMembersToIdentifiers($coGroupMembers,
$identifierType, $owners=false) {
+ // Walk through the members and pull the CO Person IDs
+
+ $coPeopleIds = array();
+
+ foreach($coGroupMembers as $m) {
+ if(($owners && $m['owner'])
+ || (!$owners && $m['member'])) {
+ $coPeopleIds[] = $m['co_person_id'];
+ }
+ }
+
+ if(!empty($coPeopleIds)) {
+ // Now perform a find to get the list. Note using the IN notation like
this
+ // may not scale to very large sets of members.
+
+ $args = array();
+ $args['conditions']['Identifier.co_person_id'] = $coPeopleIds;
+ $args['conditions']['Identifier.type'] = $identifierType;
+ $args['fields'] = array('Identifier.co_person_id',
'Identifier.identifier');
+
+ return array_values($this->CoPerson->Identifier->find('list', $args));
+ } else {
+ return array();
+ }
+ }
+
+ /**
* Update the CO Group Memberships for a CO Person.
*
* @since COmanage Registry v0.8

Modified: registry/trunk/app/Model/CoPerson.php
===================================================================
--- registry/trunk/app/Model/CoPerson.php 2013-08-21 20:36:01 UTC (rev
577)
+++ registry/trunk/app/Model/CoPerson.php 2013-08-25 23:41:19 UTC (rev
578)
@@ -83,7 +83,8 @@
'foreignKey' => 'co_person_id'
),
// A person can have many identifiers within a CO
- "Identifier" => array('dependent' => true)
+ "Identifier" => array('dependent' => true),
+ "CoProvisioningExport" => array('dependent' => true)
);

// Default display field for cake generated views

Modified: registry/trunk/app/Model/CoProvisionerPluginTarget.php
===================================================================
--- registry/trunk/app/Model/CoProvisionerPluginTarget.php 2013-08-21
20:36:01 UTC (rev 577)
+++ registry/trunk/app/Model/CoProvisionerPluginTarget.php 2013-08-25
23:41:19 UTC (rev 578)
@@ -27,17 +27,43 @@
public $name = "CoProvisionerPluginTarget";

/**
- * Determine the provisioning status of this target for a CO Person ID.
+ * Determine the provisioning status of this target for a CO Person or CO
Group.
*
* @since COmanage Registry v0.8
* @param Integer CO Provisioning Target ID
- * @param Integer CO Person ID
+ * @param Integer CO Person ID (null if CO Group ID is specified)
+ * @param Integer CO Group ID (null if CO Person ID is specified)
* @return Array ProvisioningStatusEnum, Timestamp of last update in epoch
seconds, Comment
* @throws InvalidArgumentException If $coPersonId not found
* @throws RuntimeException For other errors
*/

- abstract public function status($coProvisioningTargetId, $coPersonId);
+ public function status($coProvisioningTargetId, $coPersonId,
$coGroupId=null) {
+ // Check CoProvisioningExports for status
+
+ $ret = array(
+ 'status' => ProvisioningStatusEnum::NotProvisioned,
+ 'timestamp' => null,
+ 'comment' => ""
+ );
+
+ // Try to pull an existing record
+ $args = array();
+ $args['conditions']['CoProvisioningExport.co_provisioning_target_id'] =
$coProvisioningTargetId;
+ if($coPersonId) {
+ $args['conditions']['CoProvisioningExport.co_person_id'] = $coPersonId;
+ } else {
+ $args['conditions']['CoProvisioningExport.co_group_id'] = $coGroupId;
+ }
+ $export =
$this->CoProvisioningTarget->CoProvisioningExport->find('first', $args);
+
+ if(!empty($export)) {
+ $ret['status'] = ProvisioningStatusEnum::Provisioned;
+ $ret['timestamp'] = $export['CoProvisioningExport']['exporttime'];
+ }
+
+ return $ret;
+ }

/**
* Provision for the specified CO Person.
@@ -45,10 +71,10 @@
* @since COmanage Registry v0.8
* @param Array CO Provisioning Target data
* @param ProvisioningActionEnum Registry transaction type triggering
provisioning
- * @param Array CO Person data
+ * @param Array Provisioning data, populated with ['CoPerson'] or
['CoGroup']
* @return Boolean True on success
* @throws RuntimeException
*/

- abstract public function provision($coProvisioningTargetData, $op,
$coPersonData);
+ abstract public function provision($coProvisioningTargetData, $op,
$provisioningData);
}

Modified: registry/trunk/app/Model/CoProvisioningTarget.php
===================================================================
--- registry/trunk/app/Model/CoProvisioningTarget.php 2013-08-21 20:36:01
UTC (rev 577)
+++ registry/trunk/app/Model/CoProvisioningTarget.php 2013-08-25 23:41:19
UTC (rev 578)
@@ -35,7 +35,7 @@
// Association rules from this model to other models
public $belongsTo = array("Co");

-// public $hasMany = array("CoProvisioningQueuedEvent");
+ public $hasMany = array("CoProvisioningExport" => array('dependent' =>
true));

// Default display field for cake generated views
public $displayField = "description";

Modified:
registry/trunk/app/Plugin/ChangelogProvisioner/Config/Schema/schema.xml
===================================================================
--- registry/trunk/app/Plugin/ChangelogProvisioner/Config/Schema/schema.xml
2013-08-21 20:36:01 UTC (rev 577)
+++ registry/trunk/app/Plugin/ChangelogProvisioner/Config/Schema/schema.xml
2013-08-25 23:41:19 UTC (rev 578)
@@ -39,26 +39,4 @@
<unique />
</index>
</table>
-
- <table name="co_changelog_provisioner_exports">
- <field name="id" type="I">
- <key />
- <autoincrement />
- </field>
- <field name="co_changelog_provisioner_target_id" type="I">
- <constraint>REFERENCES
cm_co_changelog_provisioner_targets(id)</constraint>
- </field>
- <field name="co_person_id" type="I">
- <constraint>REFERENCES cm_co_people(id)</constraint>
- </field>
- <field name="exporttime" type="T" />
- <field name="created" type="T" />
- <field name="modified" type="T" />
-
- <index name="co_changelog_provisioner_targets_i1">
- <col>co_changelog_provisioner_target_id</col>
- <col>co_person_id</col>
- <unique />
- </index>
- </table>
</schema>
\ No newline at end of file

Modified:
registry/trunk/app/Plugin/ChangelogProvisioner/Model/ChangelogProvisioner.php
===================================================================
---
registry/trunk/app/Plugin/ChangelogProvisioner/Model/ChangelogProvisioner.php
2013-08-21 20:36:01 UTC (rev 577)
+++
registry/trunk/app/Plugin/ChangelogProvisioner/Model/ChangelogProvisioner.php
2013-08-25 23:41:19 UTC (rev 578)
@@ -25,13 +25,11 @@
class ChangelogProvisioner extends AppModel {
// Required by COmanage Plugins
// To enable this plugin, change the plugin type to "provisioner"
- public $cmPluginType = "disabled-provisioner";
+ public $cmPluginType = "provisioner";

// Expose Menu Items
public $cmPluginMenus = array();

// Document foreign keys
- public $cmPluginHasMany = array(
- "CoPerson" => array("CoChangelogProvisionerExport")
- );
+ public $cmPluginHasMany = array();
}

Modified:
registry/trunk/app/Plugin/ChangelogProvisioner/Model/CoChangelogProvisionerTarget.php
===================================================================
---
registry/trunk/app/Plugin/ChangelogProvisioner/Model/CoChangelogProvisionerTarget.php
2013-08-21 20:36:01 UTC (rev 577)
+++
registry/trunk/app/Plugin/ChangelogProvisioner/Model/CoChangelogProvisionerTarget.php
2013-08-25 23:41:19 UTC (rev 578)
@@ -34,8 +34,6 @@
// Association rules from this model to other models
public $belongsTo = array("CoProvisioningTarget");

- public $hasMany =
array("ChangelogProvisioner.CoChangelogProvisionerExport");
-
// Default display field for cake generated views
public $displayField = "logfile";

@@ -55,50 +53,18 @@
);

/**
- * Determine the provisioning status of this target for a CO Person ID.
- *
- * @since COmanage Registry v0.8
- * @param Integer CO Provisioning Target ID
- * @param Integer CO Person ID
- * @return Array ProvisioningStatusEnum, Timestamp of last update in epoch
seconds, Comment
- * @throws InvalidArgumentException If $coPersonId not found
- * @throws RuntimeException For other errors
- */
-
- public function status($coProvisioningTargetId, $coPersonId) {
- $ret = array(
- 'status' => ProvisioningStatusEnum::NotProvisioned,
- 'timestamp' => null,
- 'comment' => ""
- );
-
- // Try to pull an existing record
- $args = array();
-
$args['conditions']['CoChangelogProvisionerTarget.co_provisioning_target_id']
= $coProvisioningTargetId;
- $args['conditions']['CoChangelogProvisionerExport.co_person_id'] =
$coPersonId;
- $export = $this->CoChangelogProvisionerExport->find('first', $args);
-
- if(!empty($export)) {
- $ret['status'] = ProvisioningStatusEnum::Provisioned;
- $ret['timestamp'] =
$export['CoChangelogProvisionerExport']['exporttime'];
- }
-
- return $ret;
- }
-
- /**
* Provision for the specified CO Person.
*
* @since COmanage Registry v0.8
* @param Array CO Provisioning Target data
* @param ProvisioningActionEnum Registry transaction type triggering
provisioning
- * @param Array CO Person data
+ * @param Array Provisioning data, populated with ['CoPerson'] or
['CoGroup']
* @return Boolean True on success
* @throws RuntimeException
*/

- public function provision($coProvisioningTargetData, $op, $coPersonData) {
- // We pretty much ignore $op and always write a full record of
$coPersonData.
+ public function provision($coProvisioningTargetData, $op,
$provisioningData) {
+ // We pretty much ignore $op and always write a full record of
$provisioningData.

$changeLog =
$coProvisioningTargetData['CoChangelogProvisionerTarget']['logfile'];

@@ -114,33 +80,12 @@
throw new
RuntimeException(_txt('er.changelogprovisioner.logfile.lock',
array($changeLog)));
}

- fwrite($log, json_encode($coPersonData) . "\n");
+ fwrite($log, json_encode($provisioningData) . "\n");

// Release the lock and close the file
flock($log, LOCK_UN);
fclose($log);

- // Update last export time
- $data = array();
-
$data['CoChangelogProvisionerExport']['co_changelog_provisioner_target_id'] =
$coProvisioningTargetData['CoChangelogProvisionerTarget']['id'];
- $data['CoChangelogProvisionerExport']['co_person_id'] =
$coPersonData['CoPerson']['id'];
- $data['CoChangelogProvisionerExport']['exporttime'] = date('Y-m-d
H:i:s');
-
- // See if we already have a row to update
- $args = array();
-
$args['conditions']['CoChangelogProvisionerExport.co_changelog_provisioner_target_id']
= $coProvisioningTargetData['CoChangelogProvisionerTarget']['id'];
- $args['conditions']['CoChangelogProvisionerExport.co_person_id'] =
$coPersonData['CoPerson']['id'];
- $args['contain'] = false;
- $export = $this->CoChangelogProvisionerExport->find('first', $args);
-
- if(!empty($export)) {
- $data['CoChangelogProvisionerExport']['id'] =
$export['CoChangelogProvisionerExport']['id'];
- }
-
- if(!$this->CoChangelogProvisionerExport->save($data)) {
- throw new RuntimeException(_txt('er.db.save'));
- }
-
return true;
}
}

Modified: registry/trunk/app/Plugin/LdapProvisioner/Config/Schema/schema.xml
===================================================================
--- registry/trunk/app/Plugin/LdapProvisioner/Config/Schema/schema.xml
2013-08-21 20:36:01 UTC (rev 577)
+++ registry/trunk/app/Plugin/LdapProvisioner/Config/Schema/schema.xml
2013-08-25 23:41:19 UTC (rev 578)
@@ -34,13 +34,14 @@
<field name="binddn" type="C" size="128" />
<field name="password" type="C" size="64" />
<field name="basedn" type="C" size="128" />
-<!-- document these -->
<field name="dn_attribute_name" type="C" size="32" />
<field name="dn_identifier_type" type="C" size="32" />
+ <field name="group_basedn" type="C" size="128" />
<field name="opt_lang" type="L" />
<field name="opt_role" type="L" />
<field name="oc_eduperson" type="L" />
<field name="oc_edumember" type="L" />
+ <field name="oc_groupofnames" type="L" />
<field name="created" type="T" />
<field name="modified" type="T" />

@@ -61,6 +62,9 @@
<field name="co_person_id" type="I">
<constraint>REFERENCES cm_co_people(id)</constraint>
</field>
+ <field name="co_group_id" type="I">
+ <constraint>REFERENCES cm_co_groups(id)</constraint>
+ </field>
<field name="dn" type="C" size="256" />
<field name="created" type="T" />
<field name="modified" type="T" />
@@ -70,6 +74,12 @@
<col>co_person_id</col>
<unique />
</index>
+
+ <index name="co_ldap_provisioner_dns_i2">
+ <col>co_ldap_provisioner_target_id</col>
+ <col>co_group_id</col>
+ <unique />
+ </index>
</table>

<table name="co_ldap_provisioner_attributes">
@@ -81,6 +91,7 @@
<constraint>REFERENCES cm_co_ldap_provisioner_targets(id)</constraint>
</field>
<field name="attribute" type="C" size="80" />
+ <field name="objectclass" type="C" size="80" />
<field name="type" type="C" size="32" />
<field name="export" type="L" />
<field name="use_org_value" type="L" />
@@ -94,6 +105,7 @@
<index name="co_ldap_provisioner_attributes_i2">
<col>co_ldap_provisioner_target_id</col>
<col>attribute</col>
+ <col>objectclass</col>
<unique />
</index>
</table>

Modified:
registry/trunk/app/Plugin/LdapProvisioner/Controller/CoLdapProvisionerTargetsController.php
===================================================================
---
registry/trunk/app/Plugin/LdapProvisioner/Controller/CoLdapProvisionerTargetsController.php
2013-08-21 20:36:01 UTC (rev 577)
+++
registry/trunk/app/Plugin/LdapProvisioner/Controller/CoLdapProvisionerTargetsController.php
2013-08-25 23:41:19 UTC (rev 578)
@@ -75,7 +75,8 @@

$this->CoLdapProvisionerTarget->verifyLdapServer($reqdata['CoLdapProvisionerTarget']['serverurl'],

$reqdata['CoLdapProvisionerTarget']['binddn'],

$reqdata['CoLdapProvisionerTarget']['password'],
-
$reqdata['CoLdapProvisionerTarget']['basedn']);
+
$reqdata['CoLdapProvisionerTarget']['basedn'],
+
$reqdata['CoLdapProvisionerTarget']['group_basedn']);
}
catch(RuntimeException $e) {
$this->Session->setFlash($e->getMessage(), '', array(), 'error');

Modified: registry/trunk/app/Plugin/LdapProvisioner/Lib/lang.php
===================================================================
--- registry/trunk/app/Plugin/LdapProvisioner/Lib/lang.php 2013-08-21
20:36:01 UTC (rev 577)
+++ registry/trunk/app/Plugin/LdapProvisioner/Lib/lang.php 2013-08-25
23:41:19 UTC (rev 578)
@@ -35,6 +35,7 @@

// Error messages
'er.ldapprovisioner.basedn' => 'Base DN not found',
+ 'er.ldapprovisioner.basedn.gr.none' => 'When the <font
style="font-family:monospace">groupOfNames</font> object class is enabled,
the Group Base DN must be defined.',
'er.ldapprovisioner.connect' => 'Failed to connect to LDAP server',
'er.ldapprovisioner.dn.component' => 'DN component %1$s not available',
'er.ldapprovisioner.dn.config' => 'DN configuration invalid',
@@ -42,16 +43,20 @@
'er.ldapprovisioner.dn.none' => 'DN not found for CO Person %1$s',

// Plugin texts
+ 'pl.ldapprovisioner.attr.hasmember.desc' => 'Applies to Group',
+ 'pl.ldapprovisioner.attr.ismemberof.desc' => 'Applies to Person',
'pl.ldapprovisioner.attrs' => 'Attributes',
'pl.ldapprovisioner.attrs.desc' => 'Attributes to export to this LDAP
server',
- 'pl.ldapprovisioner.basedn' => 'Base DN',
- 'pl.ldapprovisioner.basedn.desc' => 'Base DN to provision entries
under',
+ 'pl.ldapprovisioner.basedn' => 'People Base DN',
+ 'pl.ldapprovisioner.basedn.desc' => 'Base DN to provision People
entries under',
+ 'pl.ldapprovisioner.basedn.gr' => 'Group Base DN',
+ 'pl.ldapprovisioner.basedn.gr.desc' => 'Base DN to provision Group entries
under (requires <font style="font-family:monospace">groupOfNames</font>
objectclass)',
'pl.ldapprovisioner.binddn' => 'Bind DN',
'pl.ldapprovisioner.binddn.desc' => 'DN to authenticate as to manage
entries',
- 'pl.ldapprovisioner.dnattr' => 'DN Attribute Name',
- 'pl.ldapprovisioner.dnattr.desc' => 'When constructing DNs, use this
attribute name for the unique component',
- 'pl.ldapprovisioner.dntype' => 'DN Identifier Type',
- 'pl.ldapprovisioner.dntype.desc' => 'When constructing DNs, use the
value associated with this identifier type as the value for the unique
component',
+ 'pl.ldapprovisioner.dnattr' => 'People DN Attribute Name',
+ 'pl.ldapprovisioner.dnattr.desc' => 'When constructing People DNs, use
this attribute name for the unique component',
+ 'pl.ldapprovisioner.dntype' => 'People DN Identifier Type',
+ 'pl.ldapprovisioner.dntype.desc' => 'When constructing People DNs, use
the value associated with this identifier type as the value for the unique
component',
'pl.ldapprovisioner.fd.useorgval' => 'Use value from Organizational
Identity',
'pl.ldapprovisioner.info' => 'The LDAP server must be available
and the specified credentials must be valid before this configuration can be
saved.',
'pl.ldapprovisioner.password' => 'Password',

Modified:
registry/trunk/app/Plugin/LdapProvisioner/Model/CoLdapProvisionerDn.php
===================================================================
--- registry/trunk/app/Plugin/LdapProvisioner/Model/CoLdapProvisionerDn.php
2013-08-21 20:36:01 UTC (rev 577)
+++ registry/trunk/app/Plugin/LdapProvisioner/Model/CoLdapProvisionerDn.php
2013-08-25 23:41:19 UTC (rev 578)
@@ -32,7 +32,8 @@
// Association rules from this model to other models
public $belongsTo = array(
"LdapProvisioner.CoLdapProvisionerTarget",
- "CoPerson"
+ "CoPerson",
+ "CoGroup"
);

// Default display field for cake generated views
@@ -47,17 +48,52 @@
),
'co_person_id' => array(
'rule' => 'numeric',
- 'required' => true,
- 'message' => 'A CO Person ID must be provided'
+ 'required' => false,
+ 'allowEmpty' => true
),
+ 'co_group_id' => array(
+ 'rule' => 'numeric',
+ 'required' => false,
+ 'allowEmpty' => true
+ ),
'dn' => array(
'rule' => 'notEmpty'
)
);

/**
- * Assign (and save) a DN for a CO Person.
+ * Assign a DN for a CO Group.
*
+ * @since COmanage Registry v0.8.2
+ * @param Array CO Provisioning Target data
+ * @param Array CO Group data
+ * @return String DN
+ * @throws RuntimeException
+ */
+
+ public function assignGroupDn($coProvisioningTargetData, $coGroupData) {
+ $dn = "";
+
+ // For now, we always construct the DN using cn.
+
+ if(empty($coGroupData['CoGroup']['name'])) {
+ throw new RuntimeException(_txt('er.ldapprovisioner.dn.component',
'cn'));
+ }
+
+
if(empty($coProvisioningTargetData['CoLdapProvisionerTarget']['group_basedn']))
{
+ // Throw an exception... this should be defined
+ throw new RuntimeException(_txt('er.ldapprovisioner.dn.config'));
+ }
+
+ $dn = "cn=" . $coGroupData['CoGroup']['name']
+ . "," .
$coProvisioningTargetData['CoLdapProvisionerTarget']['group_basedn'];
+
+ return $dn;
+ }
+
+ /**
+ * Assign a DN for a CO Person.
+ *
* @since COmanage Registry v0.8
* @param Array CO Provisioning Target data
* @param Array CO Person data
@@ -65,7 +101,7 @@
* @throws RuntimeException
*/

- public function assignDn($coProvisioningTargetData, $coPersonData) {
+ public function assignPersonDn($coProvisioningTargetData, $coPersonData) {
// Start by checking the DN configuration


if(empty($coProvisioningTargetData['CoLdapProvisionerTarget']['dn_attribute_name'])
@@ -102,16 +138,7 @@

array($coProvisioningTargetData['CoLdapProvisionerTarget']['dn_identifier_type'])));
}

- $dnRecord = array();
- $dnRecord['CoLdapProvisionerDn']['co_ldap_provisioner_target_id'] =
$coProvisioningTargetData['CoLdapProvisionerTarget']['id'];
- $dnRecord['CoLdapProvisionerDn']['co_person_id'] =
$coPersonData['CoPerson']['id'];
- $dnRecord['CoLdapProvisionerDn']['dn'] = $dn;
-
- if($this->save($dnRecord)) {
- return $dn;
- } else {
- throw new RuntimeException(_txt('er.db.save'));
- }
+ return $dn;
}

/**
@@ -120,11 +147,12 @@
* @since COmanage Registry v0.8
* @param Array CO Provisioning Target data
* @param String DN
+ * @param String Mode ('group' or 'person')
* @return Array Attribute/value pairs used to generate the DN, not
including the base DN
* @throws RuntimeException
*/

- public function dnAttributes($coProvisioningTargetData, $dn) {
+ public function dnAttributes($coProvisioningTargetData, $dn, $mode) {
// We assume dn is of the form attr1=val1, attr2=val2, basedn
// where based matches $coProvisioningTargetData. Strip off basedn
// and then split up the remaining string. Note we'll fail if the
@@ -132,8 +160,14 @@

$ret = array();

- $attrs = explode(",",
rtrim(str_replace($coProvisioningTargetData['CoLdapProvisionerTarget']['basedn'],
"", $dn), " ,"));
+ $basedn = $coProvisioningTargetData['CoLdapProvisionerTarget']['basedn'];

+ if($mode == 'group') {
+ $basedn =
$coProvisioningTargetData['CoLdapProvisionerTarget']['group_basedn'];
+ }
+
+ $attrs = explode(",", rtrim(str_replace($basedn, "", $dn), " ,"));
+
foreach($attrs as $a) {
$av = explode("=", $a, 2);

@@ -142,4 +176,136 @@

return $ret;
}
+
+ /**
+ * Map a set of CO Group Members to their DNs.
+ *
+ * @since COmanage Registry v0.8.2
+ * @param Array CO Group Members
+ * @return Array Array of DNs found -- note this array is not in any
particular order, and may have fewer entries
+ */
+
+ public function dnsForMembers($coGroupMembers) {
+ return $this->mapCoGroupMembersToDns($coGroupMembers);
+ }
+
+ /**
+ * Map a set of CO Group Member owners to their DNs.
+ *
+ * @since COmanage Registry v0.8.2
+ * @param Array CO Group Members
+ * @return Array Array of DNs found -- note this array is not in any
particular order, and may have fewer entries
+ */
+
+ public function dnsForOwners($coGroupMembers) {
+ return $this->mapCoGroupMembersToDns($coGroupMembers, true);
+ }
+
+ /**
+ * Map a set of CO Group Members to their DNs. A similar function is in
CoGroupMember.php.
+ *
+ * @since COmanage Registry v0.8.2
+ * @param Array CO Group Members
+ * @param Boolean True to map owners, false to map members
+ * @return Array Array of DNs found -- note this array is not in any
particular order, and may have fewer entries
+ */
+
+ private function mapCoGroupMembersToDns($coGroupMembers, $owners=false) {
+ // Walk through the members and pull the CO Person IDs
+
+ $coPeopleIds = array();
+
+ foreach($coGroupMembers as $m) {
+ if(($owners && $m['owner'])
+ || (!$owners && $m['member'])) {
+ $coPeopleIds[] = $m['co_person_id'];
+ }
+ }
+
+ if(!empty($coPeopleIds)) {
+ // Now perform a find to get the list. Note using the IN notation like
this
+ // may not scale to very large sets of members.
+
+ $args = array();
+ $args['conditions']['CoLdapProvisionerDn.co_person_id'] = $coPeopleIds;
+ $args['fields'] = array('CoLdapProvisionerDn.co_person_id',
'CoLdapProvisionerDn.dn');
+
+ return array_values($this->find('list', $args));
+ } else {
+ return array();
+ }
+ }
+
+ /**
+ * Obtain a DN for a provisioning subject, possibly assigning or
reassigning one.
+ *
+ * @since COmanage Registry v0.8.2
+ * @param Array CO Provisioning Target data
+ * @param Array CO Provisioning data
+ * @param String Mode: 'group' or 'person'
+ * @param Boolean Whether to assign a DN if one is not found and reassign
if the DN should be changed
+ * @return Arary An array of old and new DNs (either of which might be
null)
+ * @throws RuntimeException
+ */
+
+ public function obtainDn($coProvisioningTargetData, $provisioningData,
$mode, $assign=true) {
+ $curDn = null;
+ $newDn = null;
+
+ // First see if we have already assigned a DN
+
+ $args = array();
+ $args['conditions']['CoLdapProvisionerDn.co_ldap_provisioner_target_id']
= $coProvisioningTargetData['CoLdapProvisionerTarget']['id'];
+ if($mode == 'person') {
+ $args['conditions']['CoLdapProvisionerDn.co_person_id'] =
$provisioningData['CoPerson']['id'];
+ } else {
+ $args['conditions']['CoLdapProvisionerDn.co_group_id'] =
$provisioningData['CoGroup']['id'];
+ }
+ $args['contain'] = false;
+
+ $dnRecord = $this->find('first', $args);
+
+ if(!empty($dnRecord)) {
+ $curDn = $dnRecord['CoLdapProvisionerDn']['dn'];
+ }
+
+ if($assign) {
+ // Calculate the DN
+
+ try {
+ if($mode == 'person') {
+ $newDn = $this->assignPersonDn($coProvisioningTargetData,
$provisioningData);
+ } else {
+ $newDn = $this->assignGroupDn($coProvisioningTargetData,
$provisioningData);
+ }
+ }
+ catch(Exception $e) {
+ throw new RuntimeException($e->getMessage());
+ }
+
+ // If the the DN doesn't match the existing DN (including if there is
no
+ // existing DN), update it
+
+ if($newDn && ($curDn != $newDn)) {
+ $newDnRecord = array();
+ $newDnRecord['CoLdapProvisionerDn']['co_ldap_provisioner_target_id']
= $coProvisioningTargetData['CoLdapProvisionerTarget']['id'];
+ if($mode == 'person') {
+ $newDnRecord['CoLdapProvisionerDn']['co_person_id'] =
$provisioningData['CoPerson']['id'];
+ } else {
+ $newDnRecord['CoLdapProvisionerDn']['co_group_id'] =
$provisioningData['CoGroup']['id'];
+ }
+ $newDnRecord['CoLdapProvisionerDn']['dn'] = $newDn;
+
+ if(!empty($dnRecord)) {
+ $newDnRecord['CoLdapProvisionerDn']['id'] =
$dnRecord['CoLdapProvisionerDn']['id'];
+ }
+
+ if(!$this->save($newDnRecord)) {
+ throw new RuntimeException(_txt('er.db.save'));
+ }
+ }
+ }
+
+ return array('olddn' => $curDn, 'newdn' => $newDn);
+ }
}

Modified:
registry/trunk/app/Plugin/LdapProvisioner/Model/CoLdapProvisionerTarget.php
===================================================================
---
registry/trunk/app/Plugin/LdapProvisioner/Model/CoLdapProvisionerTarget.php
2013-08-21 20:36:01 UTC (rev 577)
+++
registry/trunk/app/Plugin/LdapProvisioner/Model/CoLdapProvisionerTarget.php
2013-08-25 23:41:19 UTC (rev 578)
@@ -96,13 +96,18 @@
*
* @since COmanage Registry v0.8
* @param Array CO Provisioning Target data
- * @param Array CO Person Data used for provisioning
+ * @param Array CO Person or CO Group Data used for provisioning
* @param Boolean Whether or not this will be for a modify operation
* @param Array Attributes used to generate the DN for this person, as
returned by CoLdapProvisionerDn::dnAttributes
* @return Array Attribute data suitable for passing to ldap_add, etc
+ * @throws UnderflowException
*/

- protected function assembleAttributes($coProvisioningTargetData,
$coPersonData, $modify, $dnAttributes) {
+ protected function assembleAttributes($coProvisioningTargetData,
$provisioningData, $modify, $dnAttributes) {
+ // First see if we're working with a Group record or a Person record
+ $person = isset($provisioningData['CoPerson']['id']);
+ $group = isset($provisioningData['CoGroup']['id']);
+
// Pull the attribute configuration
$args = array();

$args['conditions']['CoLdapProvisionerAttribute.co_ldap_provisioner_target_id']
= $coProvisioningTargetData['CoLdapProvisionerTarget']['id'];
@@ -111,7 +116,6 @@
$cAttrs = $this->CoLdapProvisionerAttribute->find('all', $args);

// Rekey the attributes array on attribute name
-
$configuredAttributes = array();

foreach($cAttrs as $a) {
@@ -128,7 +132,6 @@
$cAttrGrs = $this->CoLdapProvisionerAttrGrouping->find('all', $args);

// Rekey the attributes array on attribute name
-
$configuredAttributeGroupings = array();

foreach($cAttrGrs as $g) {
@@ -147,6 +150,12 @@
// ProvisionerBehavior will remove those from the data we get.

foreach(array_keys($supportedAttributes) as $oc) {
+ // Skip objectclasses that aren't relevant for the sort of data we're
working with
+ if(($person && $oc == 'groupOfNames')
+ || ($group && !in_array($oc, array('groupOfNames','eduMember')))) {
+ continue;
+ }
+
// Iterate across objectclasses, looking for those that are required
or enabled

if($supportedAttributes[$oc]['objectclass']['required']
@@ -184,16 +193,20 @@
switch($attr) {
// Name attributes
case 'cn':
- // Currently only preferred name supported (CO-333)
- $attributes[$attr] = generateCn($coPersonData['Name']);
+ if($person) {
+ // Currently only preferred name supported (CO-333)
+ $attributes[$attr] = generateCn($provisioningData['Name']);
+ } else {
+ $attributes[$attr] = $provisioningData['CoGroup']['name'];
+ }
break;
case 'givenName':
// Currently only preferred name supported (CO-333)
- $attributes[$attr] = $coPersonData['Name']['given'];
+ $attributes[$attr] = $provisioningData['Name']['given'];
break;
case 'sn':
// Currently only preferred name supported (CO-333)
- $attributes[$attr] = $coPersonData['Name']['family'];
+ $attributes[$attr] = $provisioningData['Name']['family'];
break;
// Attributes from CO Person Role
case 'eduPersonAffiliation':
@@ -211,7 +224,7 @@
// Walk through each role
$found = false;

- foreach($coPersonData['CoPersonRole'] as $r) {
+ foreach($provisioningData['CoPersonRole'] as $r) {
if(!empty($r[ $cols[$attr] ])) {
if($attr == 'eduPersonAffiliation') {
// Map back to the controlled vocabulary
@@ -261,14 +274,14 @@
// It's unclear what to do here if there is more than one
CoOrgIdentityLink...
// which identity do we choose? For now, the first one.

-
if(isset($coPersonData['CoOrgIdentityLink'][0]['OrgIdentity'][ $mods[$attr]
])) {
- $modelList =&
$coPersonData['CoOrgIdentityLink'][0]['OrgIdentity'][ $mods[$attr] ];
+
if(isset($provisioningData['CoOrgIdentityLink'][0]['OrgIdentity'][
$mods[$attr] ])) {
+ $modelList =&
$provisioningData['CoOrgIdentityLink'][0]['OrgIdentity'][ $mods[$attr] ];
}
- } elseif(isset($coPersonData[ $mods[$attr] ])) {
+ } elseif(isset($provisioningData[ $mods[$attr] ])) {
// Use CO Person value for this attribute
- $modelList =& $coPersonData[ $mods[$attr] ];
+ $modelList =& $provisioningData[ $mods[$attr] ];
}
- // Next: if useorgvalue reference
$coPersonData['CoOrgIdentityLink']['OrgIdentity'][ $mods[$attr] ] instead
+ // Next: if useorgvalue reference
$provisioningData['CoOrgIdentityLink']['OrgIdentity'][ $mods[$attr] ] instead
// perhaps using =& to avoid copying arrays

// Walk through each model instance
@@ -334,7 +347,7 @@
// Walk through each role, each of which can have more than
one
$found = false;

- foreach($coPersonData['CoPersonRole'] as $r) {
+ foreach($provisioningData['CoPersonRole'] as $r) {
if(isset($r[ $mods[$attr] ])) {
foreach($r[ $mods[$attr] ] as $m) {
// If a type is set, make sure it matches
@@ -361,15 +374,65 @@
$attributes[$attr] = array();
}
break;
- // Group attributes
+ // Group attributes (cn is covered above)
+ case 'description':
+ $attributes[$attr] =
$provisioningData['CoGroup']['description'];
+ break;
+ // hasMember and isMember of are both part of the eduMember
objectclass, which can apply
+ // to both people and group entries. Check what type of data
we're working with for both.
+ case 'hasMember':
+ if($group) {
+ $members = $this->CoLdapProvisionerDn
+ ->CoGroup
+ ->CoGroupMember
+
->mapCoGroupMembersToIdentifiers($provisioningData['CoGroupMember'],
+
$targetType);
+
+ if(!empty($members)) {
+ // Unlike member, hasMember is not required. However,
like owner, we can't have
+ // an empty list.
+
+ $attributes[$attr] = $members;
+ } elseif($modify) {
+ // Unless we're modifying an entry, in which case an
empty list
+ // says to remove any previous entry
+ $attributes[$attr] = array();
+ }
+ }
+ break;
case 'isMemberOf':
- foreach($coPersonData['CoGroupMember'] as $gm) {
- if(isset($gm['member']) && $gm['member']
- && !empty($gm['CoGroup']['name'])) {
- $attributes['isMemberOf'][] = $gm['CoGroup']['name'];
+ if($person) {
+ foreach($provisioningData['CoGroupMember'] as $gm) {
+ if(isset($gm['member']) && $gm['member']
+ && !empty($gm['CoGroup']['name'])) {
+ $attributes['isMemberOf'][] = $gm['CoGroup']['name'];
+ }
}
+
+ if($modify && empty($attributes[$attr])) {
+ $attributes[$attr] = array();
+ }
}
break;
+ case 'member':
+ $attributes[$attr] =
$this->CoLdapProvisionerDn->dnsForMembers($provisioningData['CoGroupMember']);
+ if(empty($attributes[$attr])) {
+ // groupofnames requires at least one member
+ throw new UnderflowException('member');
+ }
+ break;
+ case 'owner':
+ $owners =
$this->CoLdapProvisionerDn->dnsForOwners($provisioningData['CoGroupMember']);
+ if(!empty($owners)) {
+ // Can't have an empty owners list (it should either not
be present
+ // or have at least one entry)
+ $attributes[$attr] = $owners;
+ } elseif($modify) {
+ // Unless we're modifying an entry, in which case an empty
list
+ // says to remove any previous entry
+ $attributes[$attr] = array();
+ }
+ break;
default:
throw new InternalErrorException("Unknown attribute: " .
$attr);
break;
@@ -393,24 +456,28 @@
$lcattributes[strtolower($a)] = $a;
}

- // Now walk through each DN attribute
+ // Now walk through each DN attribute, but only multivalued ones.
+ // At the moment we don't check, say cn (which is single valued) even
though
+ // we probably should.

foreach(array_keys($dnAttributes) as $a) {
- // Lowercase the attribute for comparison purposes
- $lca = strtolower($a);
-
- if(isset($lcattributes[$lca])) {
- // Map back to the mixed case version
- $mca = $lcattributes[$lca];
+ if(is_array($dnAttributes[$a])) {
+ // Lowercase the attribute for comparison purposes
+ $lca = strtolower($a);

- if(empty($attributes[$mca])
- || !in_array($dnAttributes[$a], $attributes[$mca])) {
+ if(isset($lcattributes[$lca])) {
+ // Map back to the mixed case version
+ $mca = $lcattributes[$lca];
+
+ if(empty($attributes[$mca])
+ || !in_array($dnAttributes[$a], $attributes[$mca])) {
+ // Key isn't set, so store the value
+ $attributes[$a][] = $dnAttributes[$a];
+ }
+ } else {
// Key isn't set, so store the value
$attributes[$a][] = $dnAttributes[$a];
}
- } else {
- // Key isn't set, so store the value
- $attributes[$a][] = $dnAttributes[$a];
}
}

@@ -418,135 +485,26 @@
}

/**
- * Query an LDAP server.
- *
- * @since COmanage Registry v0.8
- * @param String Server URL
- * @param String Bind DN
- * @param String Password
- * @param String Base DN
- * @param String Search filter
- * @param Array Attributes to return (or null for all)
- * @return Array Search results
- * @throws RuntimeException
- */
-
- protected function queryLdap($serverUrl, $bindDn, $password, $baseDn,
$filter, $attributes=array()) {
- $ret = array();
-
- $cxn = ldap_connect($serverUrl);
-
- if(!$cxn) {
- throw new RuntimeException(_txt('er.ldapprovisioner.connect'),
LDAP_CONNECT_ERROR);
- }
-
- // Use LDAP v3 (this could perhaps become an option at some point)
- ldap_set_option($cxn, LDAP_OPT_PROTOCOL_VERSION, 3);
-
-
if(!@ldap_bind($cxn,
$bindDn, $password)) {
- throw new RuntimeException(ldap_error($cxn), ldap_errno($cxn));
- }
-
- // Try to search using base DN; look for any matching object under the
base DN
-
- $s = @ldap_search($cxn, $baseDn, $filter, $attributes);
-
- if(!$s) {
- throw new RuntimeException(ldap_error($cxn), ldap_errno($cxn));
- }
-
- $ret = ldap_get_entries($cxn, $s);
-
- ldap_unbind($cxn);
-
- return $ret;
- }
-
- /**
- * Determine the provisioning status of this target for a CO Person ID.
- *
- * @since COmanage Registry v0.8
- * @param Integer CO Provisioning Target ID
- * @param Integer CO Person ID
- * @return Array ProvisioningStatusEnum, Timestamp of last update in epoch
seconds, Comment
- * @throws InvalidArgumentException If $coPersonId not found
- * @throws RuntimeException For other errors
- */
-
- public function status($coProvisioningTargetId, $coPersonId) {
- $ret = array(
- 'status' => ProvisioningStatusEnum::Unknown,
- 'timestamp' => null,
- 'comment' => ""
- );
-
- // Pull the DN for this person, if we have one. Cake appears to
correctly interpret
- // these conditions into a JOIN.
- $args = array();
- $args['conditions']['CoLdapProvisionerTarget.co_provisioning_target_id']
= $coProvisioningTargetId;
- $args['conditions']['CoLdapProvisionerDn.co_person_id'] = $coPersonId;
-
- $dnRecord = $this->CoLdapProvisionerDn->find('first', $args);
-
- if(!empty($dnRecord)) {
- // Query LDAP and see if there is a record
- try {
- $ldapRecord =
$this->queryLdap($dnRecord['CoLdapProvisionerTarget']['serverurl'],
-
$dnRecord['CoLdapProvisionerTarget']['binddn'],
-
$dnRecord['CoLdapProvisionerTarget']['password'],
-
$dnRecord['CoLdapProvisionerDn']['dn'],
- "(objectclass=*)",
- array('modifytimestamp'));
-
- if(!empty($ldapRecord)) {
- if(!empty($ldapRecord[0]['modifytimestamp'][0])) {
- // Timestamp is formatted 20130223145645Z and needs to be
converted
- $ret['timestamp'] =
strtotime($ldapRecord[0]['modifytimestamp'][0]);
- }
-
- $ret['status'] = ProvisioningStatusEnum::Provisioned;
- $ret['comment'] = $dnRecord['CoLdapProvisionerDn']['dn'];
- } else {
- $ret['status'] = ProvisioningStatusEnum::NotProvisioned;
- $ret['comment'] = $dnRecord['CoLdapProvisionerDn']['dn'];
- }
- }
- catch(RuntimeException $e) {
- if($e->getCode() == 32) { // LDAP_NO_SUCH_OBJECT
- $ret['status'] = ProvisioningStatusEnum::NotProvisioned;
- $ret['comment'] = $dnRecord['CoLdapProvisionerDn']['dn'];
- } else {
- $ret['status'] = ProvisioningStatusEnum::Unknown;
- $ret['comment'] = $e->getMessage();
- }
- }
- } else {
- // No DN on file
-
- $ret['status'] = ProvisioningStatusEnum::NotProvisioned;
- }
-
- return $ret;
- }
-
- /**
* Provision for the specified CO Person.
*
* @since COmanage Registry v0.8
* @param Array CO Provisioning Target data
* @param ProvisioningActionEnum Registry transaction type triggering
provisioning
- * @param Array CO Person data
+ * @param Array Provisioning data, populated with ['CoPerson'] or
['CoGroup']
* @return Boolean True on success
* @throws InvalidArgumentException If $coPersonId not found
* @throws RuntimeException For other errors
*/

- public function provision($coProvisioningTargetData, $op, $coPersonData) {
+ public function provision($coProvisioningTargetData, $op,
$provisioningData) {
// First figure out what to do
$assigndn = false;
$delete = false;
$add = false;
$modify = false;
+ $rename = false;
+ $person = false;
+ $group = false;

switch($op) {
case ProvisioningActionEnum::CoPersonAdded:
@@ -555,6 +513,7 @@
$assigndn = true;
$delete = false; // Arguably, this should be true to clear out any
prior debris
$add = true;
+ $person = true;
break;
case ProvisioningActionEnum::CoPersonDeleted:
case ProvisioningActionEnum::CoPersonExpired:
@@ -562,70 +521,110 @@
$assigndn = false;
$delete = true;
$add = false;
+ $person = true;
break;
case ProvisioningActionEnum::CoPersonReprovisionRequested:
$assigndn = true;
$delete = true;
$add = true;
+ $person = true;
break;
case ProvisioningActionEnum::CoPersonUpdated:
// An update may cause an existing person to be written to LDAP for
the first time
// or for an unexpectedly removed entry to be replaced
$assigndn = true;
$modify = true;
+ $person = true;
break;
case ProvisioningActionEnum::CoPersonEnteredGracePeriod:
// We don't do anything on grace period
+ $person = true;
break;
+ case ProvisioningActionEnum::CoGroupAdded:
+ $assigndn = true;
+ $delete = false; // Arguably, this should be true to clear out any
prior debris
+ $add = true;
+ $group = true;
+ break;
+ case ProvisioningActionEnum::CoGroupDeleted:
+ $delete = true;
+ $group = true;
+ break;
+ case ProvisioningActionEnum::CoGroupUpdated:
+ $assigndn = true;
+ $modify = true;
+ $group = true;
+ break;
+ case ProvisioningActionEnum::CoGroupReprovisionRequested:
+ $assigndn = true;
+ $delete = true;
+ $add = true;
+ $group = true;
+ break;
default:
throw new RuntimeException("Not Implemented");
break;
}

- // Next, see if we already have a DN for this person
-
- $dn = null;
-
- $args = array();
- $args['conditions']['CoLdapProvisionerDn.co_ldap_provisioner_target_id']
= $coProvisioningTargetData['CoLdapProvisionerTarget']['id'];
- $args['conditions']['CoLdapProvisionerDn.co_person_id'] =
$coPersonData['CoPerson']['id'];
- $args['contain'] = false;
-
- $dnRecord = $this->CoLdapProvisionerDn->find('first', $args);
-
- if(empty($dnRecord)) {
- if($assigndn) {
- // If we don't have a DN, assign one
-
- try {
- $dn =
$this->CoLdapProvisionerDn->assignDn($coProvisioningTargetData,
$coPersonData);
- }
- catch(RuntimeException $e) {
- throw new RuntimeException($e->getMessage());
- }
+ if($group) {
+ // If this is a group action and no Group DN is defined, or
oc_groupofnames is false,
+ // then don't try to do anything.
+
+
if(!isset($coProvisioningTargetData['CoLdapProvisionerTarget']['group_basedn'])
+ ||
empty($coProvisioningTargetData['CoLdapProvisionerTarget']['group_basedn'])
+ ||
!$coProvisioningTargetData['CoLdapProvisionerTarget']['oc_groupofnames']) {
+ return true;
}
- } else {
- $dn = $dnRecord['CoLdapProvisionerDn']['dn'];
}

- if(!$dn) {
- throw new RuntimeException(_txt('er.ldapprovisioner.dn.none',
array($coPersonData['CoPerson']['id'])));
- }
+ // Next, obtain a DN for this person or group

- // Find out what attributes went into the DN to make sure they got
populated into
- // the attribute array
-
try {
- $dnAttributes =
$this->CoLdapProvisionerDn->dnAttributes($coProvisioningTargetData, $dn);
+ $dns = $this->CoLdapProvisionerDn->obtainDn($coProvisioningTargetData,
+ $provisioningData,
+ $person ? 'person' :
'group',
+ $assigndn);
}
catch(RuntimeException $e) {
throw new RuntimeException($e->getMessage());
}

- // Assemble an LDAP record
+ $dn = $dns['newdn'];

- $attributes = $this->assembleAttributes($coProvisioningTargetData,
$coPersonData, $modify, $dnAttributes);
+ // We might have to handle a rename if the DN changed

+ if($dns['olddn'] && $dns['newdn'] && ($dns['olddn'] != $dns['newdn'])) {
+ $rename = true;
+ }
+
+ if($add || $modify) {
+ // Find out what attributes went into the DN to make sure they got
populated into
+ // the attribute array
+
+ try {
+ $dnAttributes =
$this->CoLdapProvisionerDn->dnAttributes($coProvisioningTargetData, $dn,
$person ? 'person' : 'group');
+ }
+ catch(RuntimeException $e) {
+ throw new RuntimeException($e->getMessage());
+ }
+
+ // Assemble an LDAP record
+
+ try {
+ $attributes = $this->assembleAttributes($coProvisioningTargetData,
$provisioningData, $modify, $dnAttributes);
+ }
+ catch(UnderflowException $e) {
+ // We have a group with no members. Convert to a delete operation
since
+ // groupOfNames requires at least one member.
+
+ if($group) {
+ $add = false;
+ $modify = false;
+ $delete = true;
+ }
+ }
+ }
+
// Bind to the server

$cxn =
ldap_connect($coProvisioningTargetData['CoLdapProvisionerTarget']['serverurl']);
@@ -634,7 +633,8 @@
throw new RuntimeException(_txt('er.ldapprovisioner.connect'), 0x5b
/*LDAP_CONNECT_ERROR*/);
}

- // Use LDAP v3 (this could perhaps become an option at some point)
+ // Use LDAP v3 (this could perhaps become an option at some point),
although note
+ // that ldap_rename (used below) *requires* LDAP v3.
ldap_set_option($cxn, LDAP_OPT_PROTOCOL_VERSION, 3);


if(!@ldap_bind($cxn,
@@ -645,10 +645,46 @@

if($delete) {
// Delete any previous entry. For now, ignore any error.
- @ldap_delete($cxn, $dn);
+
+ if($rename) {
+ // If we're also renaming, make sure to delete using the old DN
+ @ldap_delete($cxn, $dns['olddn']);
+ } else {
+ if(!$dns['newdn']) {
+ $dn = $dns['olddn'];
+ }
+
+ @ldap_delete($cxn, $dn);
+ }
}

+ if($rename
+ // Skip this if we're doing a delete and an add, which is basically a
rename
+ && !($delete && $add)) {
+ // Perform the rename operation before we try to do anything else.
Note that
+ // the old DN is complete while the new DN is relative.
+
+ if($person) {
+ $basedn =
$coProvisioningTargetData['CoLdapProvisionerTarget']['basedn'];
+ } else {
+ $basedn =
$coProvisioningTargetData['CoLdapProvisionerTarget']['group_basedn'];
+ }
+
+ $newrdn = rtrim(str_replace($basedn, "", $dns['newdn']), " ,");
+
+
if(!@ldap_rename($cxn,
$dns['olddn'], $newrdn, null, true)) {
+ // XXX We should probably try to reset CoLdapProvisionerDn here
since we're
+ // now inconsistent with LDAP
+
+ throw new RuntimeException(ldap_error($cxn), ldap_errno($cxn));
+ }
+ }
+
if($modify) {
+ if(!$dn) {
+ throw new RuntimeException(_txt('er.ldapprovisioner.dn.none',
array($provisioningData[($person ? 'CoPerson' : 'CoGroup')]['id'])));
+ }
+

if(!@ldap_mod_replace($cxn,
$dn, $attributes)) {
if(ldap_errno($cxn) == 0x20 /*LDAP_NO_SUCH_OBJECT*/) {
// Change to an add operation. We call ourselves recursively
because
@@ -658,8 +694,10 @@
// be a pretty rare event.

$this->provision($coProvisioningTargetData,
- ProvisioningActionEnum::CoPersonAdded,
- $coPersonData);
+ ($person
+ ? ProvisioningActionEnum::CoPersonAdded
+ : ProvisioningActionEnum::CoGroupAdded),
+ $provisioningData);
} else {
throw new RuntimeException(ldap_error($cxn), ldap_errno($cxn));
}
@@ -668,6 +706,11 @@

if($add) {
// Write a new entry
+
+ if(!$dn) {
+ throw new RuntimeException(_txt('er.ldapprovisioner.dn.none',
array($provisioningData[($person ? 'CoPerson' : 'CoGroup')]['id'])));
+ }
+

if(!@ldap_add($cxn,
$dn, $attributes)) {
throw new RuntimeException(ldap_error($cxn), ldap_errno($cxn));
}
@@ -682,6 +725,129 @@
}

/**
+ * Query an LDAP server.
+ *
+ * @since COmanage Registry v0.8
+ * @param String Server URL
+ * @param String Bind DN
+ * @param String Password
+ * @param String Base DN
+ * @param String Search filter
+ * @param Array Attributes to return (or null for all)
+ * @return Array Search results
+ * @throws RuntimeException
+ */
+
+ protected function queryLdap($serverUrl, $bindDn, $password, $baseDn,
$filter, $attributes=array()) {
+ $ret = array();
+
+ $cxn = ldap_connect($serverUrl);
+
+ if(!$cxn) {
+ throw new RuntimeException(_txt('er.ldapprovisioner.connect'),
LDAP_CONNECT_ERROR);
+ }
+
+ // Use LDAP v3 (this could perhaps become an option at some point)
+ ldap_set_option($cxn, LDAP_OPT_PROTOCOL_VERSION, 3);
+
+
if(!@ldap_bind($cxn,
$bindDn, $password)) {
+ throw new RuntimeException(ldap_error($cxn), ldap_errno($cxn));
+ }
+
+ // Try to search using base DN; look for any matching object under the
base DN
+
+ $s = @ldap_search($cxn, $baseDn, $filter, $attributes);
+
+ if(!$s) {
+ throw new RuntimeException(ldap_error($cxn) . " (" . $baseDn . ")",
ldap_errno($cxn));
+ }
+
+ $ret = ldap_get_entries($cxn, $s);
+
+ ldap_unbind($cxn);
+
+ return $ret;
+ }
+
+ /**
+ * Determine the provisioning status of this target for a CO Person ID.
+ *
+ * @since COmanage Registry v0.8
+ * @param Integer CO Provisioning Target ID
+ * @param Integer CO Person ID (null if CO Group ID is specified)
+ * @param Integer CO Group ID (null if CO Person ID is specified)
+ * @return Array ProvisioningStatusEnum, Timestamp of last update in epoch
seconds, Comment
+ * @throws InvalidArgumentException If $coPersonId not found
+ * @throws RuntimeException For other errors
+ */
+
+ public function status($coProvisioningTargetId, $coPersonId,
$coGroupId=null) {
+ $ret = array(
+ 'status' => ProvisioningStatusEnum::Unknown,
+ 'timestamp' => null,
+ 'comment' => ""
+ );
+
+ // Pull the DN for this person, if we have one. Cake appears to
correctly interpret
+ // these conditions into a JOIN.
+ $args = array();
+ $args['conditions']['CoLdapProvisionerTarget.co_provisioning_target_id']
= $coProvisioningTargetId;
+ $args['conditions']['CoLdapProvisionerDn.co_person_id'] = $coPersonId;
+
+ $dnRecord = $this->CoLdapProvisionerDn->find('first', $args);
+
+ if(!empty($dnRecord)) {
+ // Query LDAP and see if there is a record
+ try {
+ $ldapRecord =
$this->queryLdap($dnRecord['CoLdapProvisionerTarget']['serverurl'],
+
$dnRecord['CoLdapProvisionerTarget']['binddn'],
+
$dnRecord['CoLdapProvisionerTarget']['password'],
+
$dnRecord['CoLdapProvisionerDn']['dn'],
+ "(objectclass=*)",
+ array('modifytimestamp'));
+
+ if(!empty($ldapRecord)) {
+ /* We don't use the LDAP timestamp anymore because another process
+ * such as Grouper may have updated it (see CO-642).
+ *
+ if(!empty($ldapRecord[0]['modifytimestamp'][0])) {
+ // Timestamp is formatted 20130223145645Z and needs to be
converted
+ $ret['timestamp'] =
strtotime($ldapRecord[0]['modifytimestamp'][0]);
+ }*/
+
+ // Get the last provision time from the parent status function
+ $pstatus = parent::status($coProvisioningTargetId, $coPersonId,
$coGroupId);
+
+ if($pstatus['status'] == ProvisioningStatusEnum::Provisioned) {
+ $ret['timestamp'] = $pstatus['timestamp'];
+ }
+
+ $ret['status'] = ProvisioningStatusEnum::Provisioned;
+ $ret['comment'] = $dnRecord['CoLdapProvisionerDn']['dn'];
+ } else {
+ $ret['status'] = ProvisioningStatusEnum::NotProvisioned;
+ $ret['comment'] = $dnRecord['CoLdapProvisionerDn']['dn'];
+ }
+ }
+ catch(RuntimeException $e) {
+ if($e->getCode() == 32) { // LDAP_NO_SUCH_OBJECT
+ $ret['status'] = ProvisioningStatusEnum::NotProvisioned;
+ $ret['comment'] = $dnRecord['CoLdapProvisionerDn']['dn'];
+ } else {
+ $ret['status'] = ProvisioningStatusEnum::Unknown;
+ $ret['comment'] = $e->getMessage();
+ }
+ }
+ } else {
+ // No DN on file
+
+ $ret['status'] = ProvisioningStatusEnum::NotProvisioned;
+ }
+
+ return $ret;
+ }
+
+ /**
* Obtain the list of attributes supported for export.
*
* @since COmanage Registry v0.8
@@ -838,14 +1004,45 @@
)
)
),
+ 'groupOfNames' => array(
+ 'objectclass' => array(
+ 'required' => false
+ ),
+ 'attributes' => array(
+ 'cn' => array(
+ 'required' => true,
+ 'multiple' => false
+ ),
+ 'member' => array(
+ 'required' => true,
+ 'multiple' => true
+ ),
+ 'owner' => array(
+ 'required' => false,
+ 'multiple' => true
+ ),
+ 'description' => array(
+ 'required' => false,
+ 'multiple' => false
+ )
+ )
+ ),
'eduMember' => array(
'objectclass' => array(
'required' => false
),
'attributes' => array(
'isMemberOf' => array(
- 'required' => true,
- 'multiple' => true
+ 'required' => false,
+ 'multiple' => true,
+ 'description' => _txt('pl.ldapprovisioner.attr.ismemberof.desc')
+ ),
+ 'hasMember' => array(
+ 'required' => false,
+ 'multiple' => true,
+ 'extendedtype' => 'identifier_types',
+ 'defaulttype' => IdentifierEnum::UID,
+ 'description' => _txt('pl.ldapprovisioner.attr.hasmember.desc')
)
)
)
@@ -861,18 +1058,29 @@
* @param String Server URL
* @param String Bind DN
* @param String Password
- * @param String Base DN
+ * @param String Base DN (People)
+ * @param String Base DN (Group)
* @return Boolean True if parameters are valid
* @throws RuntimeException
*/

- public function verifyLdapServer($serverUrl, $bindDn, $password, $baseDn) {
+ public function verifyLdapServer($serverUrl, $bindDn, $password, $baseDn,
$groupDn=null) {
$results = $this->queryLdap($serverUrl, $bindDn, $password, $baseDn,
"(objectclass=*)", array("dn"));

if(count($results) < 1) {
throw new RuntimeException(_txt('er.ldapprovisioner.basedn'));
}

+ // Check for a Group DN if one is configured
+
+ if($groupDn && $groupDn != "") {
+ $results = $this->queryLdap($serverUrl, $bindDn, $password, $groupDn,
"(objectclass=*)", array("dn"));
+
+ if(count($results) < 1) {
+ throw new
RuntimeException(_txt('er.ldapprovisioner.basedn.gr.none'));
+ }
+ }
+
return true;
}
}

Modified: registry/trunk/app/Plugin/LdapProvisioner/Model/LdapProvisioner.php
===================================================================
--- registry/trunk/app/Plugin/LdapProvisioner/Model/LdapProvisioner.php
2013-08-21 20:36:01 UTC (rev 577)
+++ registry/trunk/app/Plugin/LdapProvisioner/Model/LdapProvisioner.php
2013-08-25 23:41:19 UTC (rev 578)
@@ -31,6 +31,7 @@

// Document foreign keys
public $cmPluginHasMany = array(
+ "CoGroup" => array("CoLdapProvisionerDn"),
"CoPerson" => array("CoLdapProvisionerDn")
);
}

Modified:
registry/trunk/app/Plugin/LdapProvisioner/View/CoLdapProvisionerTargets/fields.inc
===================================================================
---
registry/trunk/app/Plugin/LdapProvisioner/View/CoLdapProvisionerTargets/fields.inc
2013-08-21 20:36:01 UTC (rev 577)
+++
registry/trunk/app/Plugin/LdapProvisioner/View/CoLdapProvisionerTargets/fields.inc
2013-08-25 23:41:19 UTC (rev 578)
@@ -23,6 +23,19 @@
*/
-->
<script type="text/javascript">
+ function js_check_group_config() {
+ // If groupOfNames objectclass is defined but Base Group DN is empty,
throw an error
+
+
if(document.getElementById('CoLdapProvisionerTargetOcGroupofnames').checked) {
+ if(document.getElementById('CoLdapProvisionerTargetGroupBasedn').value
== "") {
+ $("#error-dialog").dialog("open");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
function js_local_onload() {
<?php
// Emit javascript for each objectclass div
@@ -55,6 +68,26 @@
function toggle_div(div) {
$("#" + div).toggle("slide", { "direction" : "up" });
}
+
+ $(function() {
+ // Error dialog
+
+ $("#error-dialog").dialog({
+ autoOpen: false,
+ buttons: {
+ "<?php print _txt('op.ok'); ?>": function() {
+ $(this).dialog("close");
+ },
+ },
+ modal: true,
+ show: {
+ effect: "fade"
+ },
+ hide: {
+ effect: "fade"
+ }
+ });
+ });
</script>
<?php
// Determine if fields are editable
@@ -116,7 +149,7 @@
<table id="<?php print $this->action; ?>_co_ldap_provisioner_target"
class="ui-widget">
<tbody>
<tr class="line1">
- <td>
+ <td style="width:50%">
<?php print _txt('pl.ldapprovisioner.serverurl'); ?><font
class="required">*</font><br />
<font class="desc"><?php print
_txt('pl.ldapprovisioner.serverurl.desc'); ?></font>
</td>
@@ -186,6 +219,15 @@
</tr>
<tr class="line1">
<td>
+ <?php print _txt('pl.ldapprovisioner.basedn.gr'); ?><br />
+ <font class="desc"><?php print
_txt('pl.ldapprovisioner.basedn.gr.desc'); ?></font>
+ </td>
+ <td>
+ <?php print ($e ? $this->Form->input('group_basedn', array('size' =>
50)) :
Sanitize::html($co_ldap_provisioner_targets[0]['CoLdapProvisionerTarget']['group_basedn']));
?>
+ </td>
+ </tr>
+ <tr class="line2">
+ <td>
<?php print _txt('pl.ldapprovisioner.opts'); ?><br />
<font class="desc"><?php print _txt('pl.ldapprovisioner.opts.desc');
?></font>
</td>
@@ -203,7 +245,7 @@
?>
</td>
</tr>
- <tr class="line2">
+ <tr class="line1">
<td>
<?php print _txt('pl.ldapprovisioner.attrs'); ?><font
class="required">*</font><br />
<font class="desc"><?php print
_txt('pl.ldapprovisioner.attrs.desc'); ?></font>
@@ -320,6 +362,10 @@

print $this->Form->hidden($xprefix . '.attribute',
array('default' => $attr)) . "\n";

+ // Emit the associated object class
+
+ print $this->Form->hidden($xprefix . '.objectclass',
array('default' => $oc)) . "\n";
+
// Set up for the checkbox

$xname = 'CoLdapProvisionerAttribute.' . $xindex . '.export';
@@ -329,6 +375,10 @@
|| $currentAttributes[$attr]['export']);
$xdisabled = false;

+
if(!empty($supportedAttributes[$oc]['attributes'][$attr]['description'])) {
+ $xlabel .= " (" .
$supportedAttributes[$oc]['attributes'][$attr]['description'] . ")";
+ }
+

if($supportedAttributes[$oc]['attributes'][$attr]['required']) {
// If this attribute is required, make sure the export
field gets set
// to true when saving
@@ -411,7 +461,9 @@
<td>
<?php
if($e) {
- echo $this->Form->submit($submit_label);
+ echo $this->Form->submit($submit_label,
+ // We could also do this by adding
onsubmit to the form open tag
+ array('onclick' => 'return
js_check_group_config()'));
print $this->Form->button(_txt('op.reset'),
array('type'=>'reset'));
}
@@ -419,4 +471,8 @@
</td>
</tr>
</tbody>
-</table>
\ No newline at end of file
+</table>
+
+<div id="error-dialog" title="<?php print
_txt('pl.ldapprovisioner.basedn.gr'); ?>">
+ <p><?php print _txt('er.ldapprovisioner.basedn.gr.none'); ?></p>
+</div>
\ No newline at end of file

Modified: registry/trunk/app/View/CoGroups/fields.inc
===================================================================
--- registry/trunk/app/View/CoGroups/fields.inc 2013-08-21 20:36:01 UTC (rev
577)
+++ registry/trunk/app/View/CoGroups/fields.inc 2013-08-25 23:41:19 UTC (rev
578)
@@ -53,7 +53,41 @@

if(!$e && !$v)
return(false);
-
+
+ // Add buttons to sidebar
+ $sidebarButtons = $this->get('sidebarButtons');
+
+ if($e && !empty($co_groups[0]['CoGroup']['id'])) {
+ // Manage group memberships
+ $sidebarButtons[] = array(
+ 'icon' => 'extlink',
+ 'title' => _txt('op.manage.grm'),
+ 'url' => array(
+ 'controller' => 'co_group_members',
+ 'action' => 'select',
+ 'cogroup' => $co_groups[0]['CoGroup']['id'],
+ 'co' => $cur_co['Co']['id']
+ )
+ );
+ }
+
+ if($permissions['provision'] && !empty($co_groups[0]['CoGroup']['id'])) {
+ // Provisioning status
+ $sidebarButtons[] = array(
+ 'icon' => 'note',
+ 'title' => _txt('op.prov.view'),
+ 'url' => array(
+ 'controller' => 'co_groups',
+ 'action' => 'provision',
+ $co_groups[0]['CoGroup']['id'],
+ 'co' => $cur_co['Co']['id']
+ )
+ );
+ }
+
+ // Set buttons for rendering in sidebar
+ $this->set('sidebarButtons', $sidebarButtons);
+
// Populate the reference
echo $this->Form->hidden('co_id', array('default' =>
$cur_co['Co']['id'])). "\n";

@@ -155,13 +189,6 @@
}
}
}
-
- echo $this->Html->link(_txt('op.manage'),
- array('controller' => 'co_group_members',
- 'action' => 'select',
- 'cogroup' =>
$co_groups[0]['CoGroup']['id'],
- 'co' => $cur_co['Co']['id']),
- array('class' => 'linkbutton'));
} else {
foreach($co_group_members as $c)
echo Sanitize::html(generateCn($c['CoPerson']['Name'])) . "<br
/>\n";

Modified: registry/trunk/app/View/CoPeople/provision.ctp
===================================================================
--- registry/trunk/app/View/CoPeople/provision.ctp 2013-08-21 20:36:01
UTC (rev 577)
+++ registry/trunk/app/View/CoPeople/provision.ctp 2013-08-25 23:41:19
UTC (rev 578)
@@ -63,6 +63,7 @@
jqxhr.fail(function(jqXHR, textStatus, errorThrown) {
// Note we're getting 200 here but it's actually a success
(perhaps because no body returned)
// XXX JIRA: return content or 204 No Content instead
+ // XXX should grab error message from json body if possible

$("#progressbar-dialog").dialog("close");


Modified: registry/trunk/app/View/Layouts/default.ctp
===================================================================
--- registry/trunk/app/View/Layouts/default.ctp 2013-08-21 20:36:01 UTC (rev
577)
+++ registry/trunk/app/View/Layouts/default.ctp 2013-08-25 23:41:19 UTC (rev
578)
@@ -365,25 +365,25 @@
$f = $this->Session->flash('error');

if($f && $f != "") {
- print 'generateFlash( \''. $f . '\',"error");';
+ print "generateFlash('". str_replace("'", "\'", $f) . "',
'error');";
}

$f = $this->Session->flash('info');

if($f && $f != "") {
- print 'generateFlash( \''. $f . '\',"info");';
+ print "generateFlash('". str_replace("'", "\'", $f) . "',
'info');";
}

$f = $this->Session->flash('success');

if($f && $f != "") {
- print 'generateFlash( \''. $f . '\',"success");';
+ print "generateFlash('". str_replace("'", "\'", $f) . "',
'success');";
}

$f = $this->Session->error();

if($f && $f != "") {
- print 'generateFlash( \''. $f . '\',"error");';
+ print "generateFlash('". str_replace("'", "\'", $f) . "',
'error');";
}
?>
});



  • [comanage-dev] r578 - in registry/trunk/app: Config/Schema Controller Lib Model Model/Behavior Plugin/ChangelogProvisioner/Config/Schema Plugin/ChangelogProvisioner/Model Plugin/LdapProvisioner/Config/Schema Plugin/LdapProvisioner/Controller Plugin/LdapProvisioner/Lib Plugin/LdapProvisioner/Model Plugin/LdapProvisioner/View/CoLdapProvisionerTargets View/CoGroups View/CoPeople View/Layouts, svnlog, 08/25/2013

Archive powered by MHonArc 2.6.16.

Top of Page