Hello,
I wrote an RFC for LDAP EXOP support: https://wiki.php.net/rfc/ldap_exop
An implementation is available here: https://github.com/MCMic/php-src/tree/ldap_exop
Please feel free to comment on the chosen API or the implementation.
Any comment is welcome.
Côme
PS: Jakub Zelenka said here an RFC was not needed for this but I already started writing it so I continued, and it helps me to see the bigger picture.
But if no-one objects I think I won’t go through the voting process and merge this once people involved come to an agreement. (If there are objections we’ll go through voting, that works too.)
Hey Côme
Am 29.06.17 um 17:33 schrieb Côme Chilliet:
Hello,
I wrote an RFC for LDAP EXOP support: https://wiki.php.net/rfc/ldap_exop
An implementation is available here: https://github.com/MCMic/php-src/tree/ldap_exop
Please feel free to comment on the chosen API or the implementation.
Any comment is welcome.
Thank you for your work there!
Côme
PS: Jakub Zelenka said here an RFC was not needed for this but I already started writing it so I continued, and it helps me to see the bigger picture.
But if no-one objects I think I won’t go through the voting process and merge this once people involved come to an agreement. (If there are objections we’ll go through voting, that works too.)
If we can ommit the voting-phase we would be able to ship these changes
with PHP 7.2 and would not need to wait for PHP 7.3/8.0. As the changes
do not change the language itself but add long awaited features for
LDAP-Interaction I'd love to see this in PHP 7.2
Cheers
Andreas
--
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| http://andreas.heigl.org http://hei.gl/wiFKy7 |
+---------------------------------------------------------------------+
| http://hei.gl/root-ca |
+---------------------------------------------------------------------+
Hello,
please change the signature form
bool ldap_exop_whoami(resource $link, string &$result)
to
string ldap_whoami(resource $link);
i dont see the benefit to return a single value with a reference.
Without a reference the function can used as an argument:
doSomethingWithTheUserName(ldap_whoami($link));
With a reference a variable is required:
ldap_whoami($link, $username);
doSomethingWithTheUserName($username);
same with the return statement of a method:
class MyLdap
{
public function getCurrentUser(): string
{
return ldap_whoami($this->link);
}
}
class MyLdap
{
function getCurrentUser(): string
{
ldap_whoami($this->link, $username);
return $username;
}
}
Le lundi 3 juillet 2017, 17:04:02 Andreas Treichel a écrit :
i dont see the benefit to return a single value with a reference.
Without a reference the function can used as an argument:
doSomethingWithTheUserName(ldap_whoami($link));
Where do you handle errors in this case?
For me returning a mixed will result in:
$identity = ldap_exop_whoami($link);
if ($identity !== FALSE) {
// do something with $identity
} else {
// handle errors
}
While with the reference it’s cleaner:
if (ldap_exop_whoami($link, $identity)) {
// do something with $identity
} else {
// handle errors
}
It seems both solutions exists in existing LDAP methods:
https://secure.php.net/manual/en/function.ldap-get-option.php
https://secure.php.net/manual/en/function.ldap-get-dn.php
The other thing I liked about using bool return and a reference is that it will allow all ldap_exop_* methods to be consistent, all returning a boolean and filling as many reference parameters as needed (from 0 to whatever).
same with the return statement of a method:
class MyLdap
{
public function getCurrentUser(): string
{
return ldap_whoami($this->link);
}
}class MyLdap
{
function getCurrentUser(): string
{
ldap_whoami($this->link, $username);
return $username;
}
}
This is a valid point.
Côme
Hey Côme, hey Andreas.
Am 03.07.17 um 17:04 schrieb Andreas Treichel:
Hello,
please change the signature form
bool ldap_exop_whoami(resource $link, string &$result)
to
string ldap_whoami(resource $link);
i dont see the benefit to return a single value with a reference.
Without a reference the function can used as an argument:
doSomethingWithTheUserName(ldap_whoami($link));With a reference a variable is required:
ldap_whoami($link, $username);
doSomethingWithTheUserName($username);same with the return statement of a method:
class MyLdap
{
public function getCurrentUser(): string
{
return ldap_whoami($this->link);
}
}class MyLdap
{
function getCurrentUser(): string
{
ldap_whoami($this->link, $username);
return $username;
}
}
After thinking a while about this I think it would be a good idea to
alter the signature of the functions and return a value or FALSE
instead
of a boolean value.
In my eyes the code and the extension would benefit in a few ways:
- It would allow us to slowly get rid of pass-by-reference variables
- It would make it easier for users to get a value
- It would allow us to shift to use Exceptions instead of errors more easily
For methods that could return more than one value we could return an
array or an object.
There are currently both ways in the code but from the discussions I've
had so far I've learned that removing the references (and getting rid of
the resources) would be a plus.
From what I've seen in the code at least removing the pass-by-reference
should be possible without too much changes.
So the signatures would look like this:
string|FALSE ldap_exop_whoami(resource $link) - The returned string is
the DN of the currently bound user.
string|FALSE ldap_exop_passwd(resource $link [, string $user [, string
$oldPassword [, string $newPassword]]] - The returned string is the new
password of the user. Either the given newPassword or a newly generated one.
Both would return FALSE
on an error (or throw an Exception) so one can
use them like this
if (false === ($password = ldap_exop_passwd($link))) {
// something went wrong
} else {
echo $password;
}
Or is that complete and utter nonsense?
Cheers
Andreas
--
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| http://andreas.heigl.org http://hei.gl/wiFKy7 |
+---------------------------------------------------------------------+
| http://hei.gl/root-ca |
+---------------------------------------------------------------------+
Hey Côme, hey Andreas.
string|FALSE ldap_exop_whoami(resource $link) - The returned string is
the DN of the currently bound user.
In my opinion the code is really ease to read with exceptions.
try {
$user = ldap_exop_whoami($link);
}
catch(Throwable $e) {
}
string|FALSE ldap_exop_passwd(resource $link [, string $user [, string
$oldPassword [, string $newPassword]]] - The returned string is the new
password of the user. Either the given newPassword or a newly generated one.
Change password of current user with a random password.
ldap_exop_passwd($link);
Change password of $user with a random password.
ldap_exop_passwd($link, $user);
Change $oldPassword of $user with a random password.
ldap_exop_passwd($link, $user, $oldPassword);
Change $oldPassword of $user to $newPassword.
ldap_exop_passwd($link, $user, $oldPassword, $newPassword);
As i wrote the four samples, i actually already like the ordering of the
arguments as it seems to make sense.
How is the behavior of the following?
Change $oldPassword of current user to $newPassword?
ldap_exop_passwd($link, '', $oldPassword, $newPassword);
Change $oldPassword of $user to empty string? Or random? Or is this an
error?
ldap_exop_passwd($link, $user, $oldPassword, '');
My previous suggestion was to split the function into two versions to
reduce the amount of arguments.
string|FALSE ldap_exop_passwd(resource $link, string $user, string
$newPassword [, string $oldPassword])
string|FALSE ldap_exop_random_passwd(resource $link, string $user [,
string $oldPassword])
Or is that complete and utter nonsense?
Maybe, so lets talk about the functions. i dont like the unnecessary
references. But if the references makes more sense in this situation,
please use them.
Hey Andreas.
Am 04.07.17 um 00:16 schrieb Andreas Treichel:
Hey Côme, hey Andreas.
string|FALSE ldap_exop_whoami(resource $link) - The returned string is
the DN of the currently bound user.In my opinion the code is really ease to read with exceptions.
try {
$user = ldap_exop_whoami($link);
}
catch(Throwable $e) {
}
It definitely is easier to read. But let's not try too much in one go.
As all of the current extension uses errors I'd currently stick to the
errors and leave moving to extensions for a later change. Partly to keep
at least some kind of consistency and partly to not come into the trap
of moving to extensions completely and therefore breaking BC…
string|FALSE ldap_exop_passwd(resource $link [, string $user [, string
$oldPassword [, string $newPassword]]] - The returned string is the new
password of the user. Either the given newPassword or a newly
generated one.Change password of current user with a random password.
ldap_exop_passwd($link);Change password of $user with a random password.
ldap_exop_passwd($link, $user);Change $oldPassword of $user with a random password.
ldap_exop_passwd($link, $user, $oldPassword);Change $oldPassword of $user to $newPassword.
ldap_exop_passwd($link, $user, $oldPassword, $newPassword);As i wrote the four samples, i actually already like the ordering of the
arguments as it seems to make sense.How is the behavior of the following?
Change $oldPassword of current user to $newPassword?
ldap_exop_passwd($link, '', $oldPassword, $newPassword)
ldap_exop_passwd($link, null, $oldPassword, $newPassword);
Though passing an empty string should work also with the current code.
But I'd prefer to pass NULL
Change $oldPassword of $user to empty string? Or random? Or is this an
error?
ldap_exop_passwd($link, $user, $oldPassword, '');
IMHO you can't change to an empty string. Because that would be like not
setting a password at all. I'd restrict that so far that providing an
empty password will cause the server to generate a random password that
is then returned.
My previous suggestion was to split the function into two versions to
reduce the amount of arguments.string|FALSE ldap_exop_passwd(resource $link, string $user, string
$newPassword [, string $oldPassword])string|FALSE ldap_exop_random_passwd(resource $link, string $user [,
string $oldPassword])
I would not do that as it bloats the API in an - in my eyes -
unnecessary way. Let's stick to one function for changing password…
One thing though that I thought about: Chapter 4 of RFC 3062 explicitly
states that this function should only be available with confidentially
support like TLS. So perhaps we should check whether the data will be
transfered via a secure connection and - if not - raise an error?
Cheers
Andreas
--
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| http://andreas.heigl.org http://hei.gl/wiFKy7 |
+---------------------------------------------------------------------+
| http://hei.gl/root-ca |
+---------------------------------------------------------------------+
Le mardi 4 juillet 2017, 07:03:20 Andreas Heigl a écrit :
In my opinion the code is really ease to read with exceptions.
try {
$user = ldap_exop_whoami($link);
}
catch(Throwable $e) {
}It definitely is easier to read. But let's not try too much in one go.
As all of the current extension uses errors I'd currently stick to the
errors and leave moving to extensions for a later change. Partly to keep
at least some kind of consistency and partly to not come into the trap
of moving to extensions completely and therefore breaking BC…
For me moving to exception is an other subject that would need to be treated separately.
And I’m not even sure I would vote for that.
How is the behavior of the following?
Change $oldPassword of current user to $newPassword?
ldap_exop_passwd($link, '', $oldPassword, $newPassword)
ldap_exop_passwd($link, null, $oldPassword, $newPassword);Though passing an empty string should work also with the current code.
But I'd prefer to passNULL
Change $oldPassword of $user to empty string? Or random? Or is this an
error?
ldap_exop_passwd($link, $user, $oldPassword, '');IMHO you can't change to an empty string. Because that would be like not
setting a password at all. I'd restrict that so far that providing an
empty password will cause the server to generate a random password that
is then returned.
This change to a random password.
My previous suggestion was to split the function into two versions to
reduce the amount of arguments.string|FALSE ldap_exop_passwd(resource $link, string $user, string
$newPassword [, string $oldPassword])string|FALSE ldap_exop_random_passwd(resource $link, string $user [,
string $oldPassword])I would not do that as it bloats the API in an - in my eyes -
unnecessary way. Let's stick to one function for changing password…
Yeah same here, one function for each EXOP makes sense and I don’t see the point of splitting into two functions.
I don’t like the idea of switching oldpw and newpw order either as it may confuse people.
One thing though that I thought about: Chapter 4 of RFC 3062 explicitly
states that this function should only be available with confidentially
support like TLS. So perhaps we should check whether the data will be
transfered via a secure connection and - if not - raise an error?
Hum I get the idea but is that really our place? I mean the API won’t prevent you from storing password without hashing for instance.
And people can use ldap_modify to change the password without TLS, which is equally dangerous IMO.
For me it should be possible, and useful at least for tests.
So for the API I will move to
string|FALSE ldap_exop_whoami(resource $link) - The returned string is
the DN of the currently bound user.
For ldap_exop_passwd, you proposed:
string|FALSE ldap_exop_passwd(resource $link [, string $user [, string
$oldPassword [, string $newPassword]]] - The returned string is the new
password of the user. Either the given newPassword or a newly generated one.
Does it makes sense to return the newPassword on success when it’s not generated? I think I would prefer if returning a string explicitely means a password was generated, otherwise it gets too confused (3 possible meaning for the returned value!).
Côme
Hey Côme
Am 04.07.17 um 09:19 schrieb Côme Chilliet:
[...]
For ldap_exop_passwd, you proposed:
string|FALSE ldap_exop_passwd(resource $link [, string $user [, string
$oldPassword [, string $newPassword]]] - The returned string is the new
password of the user. Either the given newPassword or a newly generated one.Does it makes sense to return the newPassword on success when it’s not generated? I think I would prefer if returning a string explicitely means a password was generated, otherwise it gets too confused (3 possible meaning for the returned value!).
I'd always return the new password. In userland users can compare the
password passed in to the returned password to see whether it was
generated or altered in a way. I think it makes it more consistent than
having to check for three values (FALSE, null|'' and a string).
Cheers
Andreas
--
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| http://andreas.heigl.org http://hei.gl/wiFKy7 |
+---------------------------------------------------------------------+
| http://hei.gl/root-ca |
+---------------------------------------------------------------------+
Hello,
One thing though that I thought about: Chapter 4 of RFC 3062 explicitly
states that this function should only be available with confidentially
support like TLS. So perhaps we should check whether the data will be
transfered via a secure connection and - if not - raise an error?
Hum I get the idea but is that really our place? I mean the API won’t prevent you from storing password without hashing for instance.
And people can use ldap_modify to change the password without TLS, which is equally dangerous IMO.
For me it should be possible, and useful at least for tests.
Prefer TLS is good, but is TLS also required on internal networks (e.g.
docker)?
Am 04.07.17 um 10:19 schrieb Andreas Treichel:
Hello,
One thing though that I thought about: Chapter 4 of RFC 3062 explicitly
states that this function should only be available with confidentially
support like TLS. So perhaps we should check whether the data will be
transfered via a secure connection and - if not - raise an error?Hum I get the idea but is that really our place? I mean the API won’t
prevent you from storing password without hashing for instance.
And people can use ldap_modify to change the password without TLS,
which is equally dangerous IMO.
For me it should be possible, and useful at least for tests.Prefer TLS is good, but is TLS also required on internal networks (e.g.
docker)?
The RFC[1] is pretty strict on that one. "This extension MUST be used
with confidentiality protection, such as Start TLS [RFC 2830]."
So TLS is not a requirement per se but confidentiality protection…
So I wouldn't check whether TLS is in place as f.e. docker might be a
good confidentiality protection as well…
Cheers
Andreas
--
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| http://andreas.heigl.org http://hei.gl/wiFKy7 |
+---------------------------------------------------------------------+
| http://hei.gl/root-ca |
+---------------------------------------------------------------------+