Use schema Attribute in Entry to convert value from/to LDAP from/to PHP

This commit is contained in:
Benjamin Renard 2023-03-17 20:02:52 +01:00
parent a5b19f39cc
commit bb68b10ec4
6 changed files with 236 additions and 22 deletions

View file

@ -60,7 +60,7 @@ class Entry {
case 'changes': case 'changes':
return $this -> changes; return $this -> changes;
default: default:
return $this -> get_values($key); return $this -> get_value($key);
} }
} }
@ -78,14 +78,7 @@ class Entry {
else else
$this -> error('Unexcepted DN value provided: must be a string or false'); $this -> error('Unexcepted DN value provided: must be a string or false');
default: default:
$key = mb_strtolower($key); $this -> set_value($key, $value);
if (is_null($value))
$value = array();
if (!is_array($value))
$value = array($value);
$this -> changes[$key] = array();
foreach($value as $v)
$this -> changes[$key][] = strval($v);
} }
} }
@ -103,7 +96,7 @@ class Entry {
} }
/** /**
* Get an attribute values * Get raw attribute values
* @param string $attr The expected attribute name * @param string $attr The expected attribute name
* @param mixed $default The default value if attribute is not set * @param mixed $default The default value if attribute is not set
* (optional, default: null or array() if $all_values is True) * (optional, default: null or array() if $all_values is True)
@ -111,7 +104,7 @@ class Entry {
* first one (optional, default: true) * first one (optional, default: true)
* @return ( $all_values is True ? array<string> : mixed ) * @return ( $all_values is True ? array<string> : mixed )
*/ */
public function get_values($attr, $default=null, $all_values=true) { public function get_raw_values($attr, $default=null, $all_values=true) {
$attr = mb_strtolower($attr); $attr = mb_strtolower($attr);
$values = false; $values = false;
if (array_key_exists($attr, $this -> changes)) if (array_key_exists($attr, $this -> changes))
@ -123,6 +116,71 @@ class Entry {
return $all_values?$values:$values[0]; return $all_values?$values:$values[0];
} }
/**
* Get an attribute value (using LDAP schema if used and a LDAP link is registered on entry)
* @param string $attr The expected attribute name
* @param mixed $default The default value if attribute is not set
* (optional, default: null or array() if $all_values is True)
* @param bool $first If True, return only the first attribute values, otherwise return
* depending on the fact is single valued or not in LDAP schema (optional,
* default: false)
* @return mixed
*/
public function get_value($attr, $default=null, $first=false) {
if (!$this -> ldap || !$this -> ldap -> schema)
return $this -> get_raw_values($attr, $default, $first?false:true);
$attr = mb_strtolower($attr);
$values = false;
if (array_key_exists($attr, $this -> changes))
$values = &$this -> changes[$attr];
elseif (array_key_exists($attr, $this -> data))
$values = &$this -> data[$attr];
$attribute = $this -> ldap -> schema -> attribute($attr);
if (!$attribute)
return $this -> error('Unkown attribute %s in schema', null, $attr);
if (!$values) {
if (is_null($default) && !$first && !$attribute->single)
return array();
return $default;
}
return $attribute -> ldap2php($values);
}
/**
* Set raw LDAP attribute values
* @param string $attr
* @param mixed $values
* @return void
*/
public function set_raw_values($attr, $values) {
$attr = mb_strtolower($attr);
if (is_null($values))
$values = array();
else if (!is_array($values))
$values = array($values);
$this -> changes[$attr] = array();
foreach($values as $value)
$this -> changes[$attr][] = strval($value);
}
/**
* Set LDAP attribute values
* @param string $attr
* @param mixed $value
* @return void|false
*/
public function set_value($attr, $value) {
if (!$this -> ldap || !$this -> ldap -> schema) {
$this -> set_raw_values($attr, $value);
return;
}
$attribute = $this -> ldap -> schema -> attribute($attr);
if (!$attribute)
return $this -> error('Unkown attribute %s in schema', null, $attr);
$values = $attribute -> php2ldap($value);
$this -> set_raw_values($attr, $values);
}
/** /**
* Log and eventually raise an error * Log and eventually raise an error
* @param string $error The error message * @param string $error The error message

View file

@ -21,6 +21,7 @@ namespace EesyLDAP;
* @property-read string $scope * @property-read string $scope
* @property-read bool $raise_on_error * @property-read bool $raise_on_error
* @property-read bool $use_schema * @property-read bool $use_schema
* @property-read Schema|false $schema
*/ */
class Ldap { class Ldap {
@ -391,12 +392,11 @@ class Ldap {
case 'default_config': case 'default_config':
return self :: $default_config; return self :: $default_config;
case 'schema': case 'schema':
if ($this->schema)
return $this->schema;
if (!$this->use_schema) if (!$this->use_schema)
return false; return false;
$this->schema = Schema :: load($this); if (!$this->schema)
return $this->schema; $this->schema = Schema :: load($this);
return $this->schema && $this->schema->loaded?$this->schema:false;
} }
return $this -> error("Invalid property '$key' requested"); return $this -> error("Invalid property '$key' requested");
} }
@ -506,7 +506,7 @@ class Ldap {
$attrs[$attr][$k] = $result[$i][$attr][$k]; $attrs[$attr][$k] = $result[$i][$attr][$k];
} }
// @phpstan-ignore-next-line // @phpstan-ignore-next-line
$entries[$dn] = new Entry($dn, $attrs); $entries[$dn] = new Entry($dn, $attrs, $this);
} }
// @phpstan-ignore-next-line // @phpstan-ignore-next-line
return $entries; return $entries;

View file

@ -2,6 +2,9 @@
namespace EesyLDAP; namespace EesyLDAP;
/**
* @property-read bool $loaded
*/
class Schema { class Schema {
/** /**
@ -53,6 +56,12 @@ class Schema {
*/ */
protected $oids = array(); protected $oids = array();
/**
* Loaded telltale
* @var bool
*/
protected $loaded = false;
/** /**
* Constructor * Constructor
* @param Ldap $ldap The LDAP connection * @param Ldap $ldap The LDAP connection
@ -63,6 +72,23 @@ class Schema {
$this -> ldap = $ldap; $this -> ldap = $ldap;
$this -> entry = $entry; $this -> entry = $entry;
$this -> parse(); $this -> parse();
$this -> loaded = true;
}
/**
* Magic method to get schema key
* @param string $key
* @return mixed
* @throws \EesyLDAP\InvalidPropertyException
*/
public function __get($key) {
switch ($key) {
case 'loaded':
return $this -> loaded;
}
throw new \EesyLDAP\InvalidPropertyException(
"Invalid property '$key' requested on '".get_called_class()."'"
);
} }
/** /**
@ -88,7 +114,7 @@ class Schema {
protected function parse() { protected function parse() {
foreach (self :: $attributes_to_entry_types as $attr => $type) { foreach (self :: $attributes_to_entry_types as $attr => $type) {
$type_name = strtolower($type); $type_name = strtolower($type);
foreach($this -> entry -> get_values($attr, array(), true) as $value) { foreach($this -> entry -> get_raw_values($attr, array(), true) as $value) {
// @phpstan-ignore-next-line // @phpstan-ignore-next-line
$entry = call_user_func("\\EesyLDAP\\Schema\\$type::parse", $value); $entry = call_user_func("\\EesyLDAP\\Schema\\$type::parse", $value);
if (!$entry instanceof Schema\SchemaEntry) { if (!$entry instanceof Schema\SchemaEntry) {

View file

@ -2,6 +2,7 @@
namespace EesyLDAP\Schema; namespace EesyLDAP\Schema;
use EesyLDAP\Schema;
/** /**
* @property-read string $oid * @property-read string $oid
@ -69,6 +70,32 @@ class Attribute extends SchemaEntry {
'multiple', 'multiple',
); );
/**
* Map attribute syntax OID to corresponding sub Attribute types
* @var array<string,class-string<Attribute>>
*/
protected static $map_syntax_to_subtypes = array(
Schema::SYNTAX_BOOLEAN => Attribute\BooleanAttribute::class,
);
/**
* Parse a SubSchema attribute value into a SchemaEntry object
* @param string $value Attribute value
* @return Attribute
*/
public static function parse($value) {
$schema_entry = self :: _parse($value);
if (
isset($schema_entry['syntax'])
&& is_string($schema_entry['syntax'])
&& array_key_exists($schema_entry['syntax'], self :: $map_syntax_to_subtypes)
)
$type = self :: $map_syntax_to_subtypes[$schema_entry['syntax']];
else
$type = self :: class;
return new $type($schema_entry);
}
/** /**
* Magic method to get attribute schema entry key * Magic method to get attribute schema entry key
* @param string $key * @param string $key
@ -82,4 +109,31 @@ class Attribute extends SchemaEntry {
} }
return parent::__get($key); return parent::__get($key);
} }
/**
* Convert LDAP value to PHP value
* @param array<int,string> $value
* @return string|array<string>|null
*/
public function ldap2php($value) {
if ($value)
return $this->single?$value[0]:$value;
return $this->single?null:array();
}
/**
* Convert LDAP value to PHP value
* @param mixed $value
* @return array<int,string>
*/
public function php2ldap($value) {
if (is_null($value))
return array();
if (!is_array($value))
$value = array($value);
$ldap_value = array();
foreach($value as $v)
$ldap_value[] = strval($v);
return $ldap_value;
}
} }

View file

@ -0,0 +1,68 @@
<?php
namespace EesyLDAP\Schema\Attribute;
/**
* LDAP Schema boolean attribute
*/
class BooleanAttribute extends \EesyLDAP\Schema\Attribute {
/**
* LDAP stored value for true
* @var string
*/
const TRUE_VALUE = 'TRUE';
/**
* LDAP stored value for false
* @var string
*/
const FALSE_VALUE = 'FALSE';
/**
* Convert one LDAP value to PHP value
* @param string $value
* @return bool|null
*/
protected function _ldap2php($value) {
if ($value == self :: TRUE_VALUE)
return true;
if ($value == self :: FALSE_VALUE)
return false;
return null;
}
/**
* Convert LDAP value to PHP value
* @param array<int,string> $value
* @return bool|null|array<bool|null>
*/
public function ldap2php($value) {
$value = parent::ldap2php($value);
if (is_string($value))
return self :: _ldap2php($value);
if (is_array($value)) {
$php_value = array();
foreach($value as $v)
$php_value[] = self :: _ldap2php($v);
return $php_value;
}
return $this->single?null:array();
}
/**
* Convert LDAP value to PHP value
* @param mixed $value
* @return array<int,string>
*/
public function php2ldap($value) {
if (is_null($value))
return array();
$value = is_array($value)?$value:array($value);
$ldap_value = array();
foreach(parent::php2ldap($value) as $value)
$ldap_value[] = $value?self :: TRUE_VALUE:self :: FALSE_VALUE;
return $ldap_value;
}
}

View file

@ -50,17 +50,15 @@ class SchemaEntry {
} }
/** /**
* Parses an SubSchema attribute value into a SchemaEntry object * Parse an SubSchema attribute value into a hash of entry properties
* *
* Note: mainly from PEAR Net_LDAP2_Schema::_parse_entry() * Note: mainly from PEAR Net_LDAP2_Schema::_parse_entry()
* @link https://pear.php.net/package/Net_LDAP2 * @link https://pear.php.net/package/Net_LDAP2
* *
* @param string $value Attribute value * @param string $value Attribute value
* * @return array<string,mixed>
* @access protected
* @return SchemaEntry
*/ */
public static function parse($value) { public static function _parse($value) {
// tokens that have no value associated // tokens that have no value associated
$noValue = array( $noValue = array(
'single-value', 'single-value',
@ -116,6 +114,16 @@ class SchemaEntry {
$schema_entry['max_length'] = intval($matches[1]); $schema_entry['max_length'] = intval($matches[1]);
} }
} }
return $schema_entry;
}
/**
* Parse a SubSchema attribute value into a SchemaEntry object
* @param string $value Attribute value
* @return SchemaEntry|false
*/
public static function parse($value) {
$schema_entry = self :: _parse($value);
$type = get_called_class(); $type = get_called_class();
return new $type($schema_entry); return new $type($schema_entry);
} }