Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117884144
LDAP.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
10 KB
Referenced Files
None
Subscribers
None
LDAP.php
View Options
<?php
/**
* Storage backend to store 2-Factor-Authentication settings in LDAP
*
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2015, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace
Kolab2FA\Storage
;
use
\Net_LDAP3
;
use
\Kolab2FA\Log\Logger
;
class
LDAP
extends
Base
{
public
$userdn
;
private
$cache
=
array
();
private
$ldapcache
=
array
();
private
$conn
;
private
$error
;
public
function
init
(
array
$config
)
{
parent
::
init
(
$config
);
$this
->
conn
=
new
Net_LDAP3
(
$config
);
$this
->
conn
->
config_set
(
'log_hook'
,
array
(
$this
,
'log'
));
$this
->
conn
->
connect
();
$bind_pass
=
$this
->
config
[
'bind_pass'
];
$bind_user
=
$this
->
config
[
'bind_user'
];
$bind_dn
=
$this
->
config
[
'bind_dn'
];
$this
->
ready
=
$this
->
conn
->
bind
(
$bind_dn
,
$bind_pass
);
if
(!
$this
->
ready
)
{
throw
new
Exception
(
"LDAP storage not ready: "
.
$this
->
error
);
}
}
/**
* List/set methods activated for this user
*/
public
function
enumerate
(
$active
=
true
)
{
$filter
=
$this
->
parse_vars
(
$this
->
config
[
'filter'
],
'*'
);
$base_dn
=
$this
->
parse_vars
(
$this
->
config
[
'base_dn'
],
'*'
);
$scope
=
$this
->
config
[
'scope'
]
?:
'sub'
;
$ids
=
array
();
if
(
$this
->
ready
&&
(
$result
=
$this
->
conn
->
search
(
$base_dn
,
$filter
,
$scope
,
array
(
$this
->
config
[
'fieldmap'
][
'id'
],
$this
->
config
[
'fieldmap'
][
'active'
]))))
{
foreach
(
$result
as
$dn
=>
$entry
)
{
$rec
=
$this
->
field_mapping
(
$dn
,
Net_LDAP3
::
normalize_entry
(
$entry
,
true
));
if
(!
empty
(
$rec
[
'id'
])
&&
(
$active
===
null
||
$active
==
$rec
[
'active'
]))
{
$ids
[]
=
$rec
[
'id'
];
}
}
}
// TODO: cache this in memory
return
$ids
;
}
/**
* Read data for the given key
*/
public
function
read
(
$key
)
{
if
(!
isset
(
$this
->
cache
[
$key
]))
{
$this
->
cache
[
$key
]
=
$this
->
get_ldap_record
(
$this
->
username
,
$key
);
}
return
$this
->
cache
[
$key
];
}
/**
* Save data for the given key
*/
public
function
write
(
$key
,
$value
)
{
$success
=
false
;
$ldap_attrs
=
array
();
if
(
is_array
(
$value
))
{
// add some default values
$value
+=
(
array
)
$this
->
config
[
'defaults'
]
+
array
(
'active'
=>
false
,
'username'
=>
$this
->
username
,
'userdn'
=>
$this
->
userdn
);
foreach
(
$value
as
$k
=>
$val
)
{
if
(
$attr
=
$this
->
config
[
'fieldmap'
][
$k
])
{
$ldap_attrs
[
$attr
]
=
$this
->
value_mapping
(
$k
,
$val
,
false
);
}
}
}
else
{
// invalid data structure
return
false
;
}
// update existing record
if
(
$rec
=
$this
->
get_ldap_record
(
$this
->
username
,
$key
))
{
$old_attrs
=
$rec
[
'_raw'
];
$new_attrs
=
array_merge
(
$old_attrs
,
$ldap_attrs
);
$result
=
$this
->
conn
->
modify_entry
(
$rec
[
'_dn'
],
$old_attrs
,
$new_attrs
);
$success
=
!
empty
(
$result
);
}
// insert new record
else
if
(
$this
->
ready
)
{
$entry_dn
=
$this
->
get_entry_dn
(
$this
->
username
,
$key
);
// add object class attribute
$me
=
$this
;
$ldap_attrs
[
'objectclass'
]
=
array_map
(
function
(
$cls
)
use
(
$me
,
$key
)
{
return
$me
->
parse_vars
(
$cls
,
$key
);
},
(
array
)
$this
->
config
[
'objectclass'
]);
$success
=
$this
->
conn
->
add_entry
(
$entry_dn
,
$ldap_attrs
);
}
if
(
$success
)
{
$this
->
cache
[
$key
]
=
$value
;
$this
->
ldapcache
=
array
();
// cleanup: remove disabled/inactive/temporary entries
if
(
$value
[
'active'
])
{
foreach
(
$this
->
enumerate
(
false
)
as
$id
)
{
if
(
$id
!=
$key
)
{
$this
->
remove
(
$id
);
}
}
// set user roles according to active factors
$this
->
set_user_roles
();
}
}
return
$success
;
}
/**
* Remove the data stored for the given key
*/
public
function
remove
(
$key
)
{
if
(
$this
->
ready
)
{
$entry_dn
=
$this
->
get_entry_dn
(
$this
->
username
,
$key
);
$success
=
$this
->
conn
->
delete_entry
(
$entry_dn
);
// set user roles according to active factors
if
(
$success
)
{
$this
->
set_user_roles
();
}
return
$success
;
}
return
false
;
}
/**
* Set username to store data for
*/
public
function
set_username
(
$username
)
{
parent
::
set_username
(
$username
);
// reset cached values
$this
->
cache
=
array
();
$this
->
ldapcache
=
array
();
}
/**
*
*/
protected
function
set_user_roles
()
{
if
(!
$this
->
ready
||
!
$this
->
userdn
||
empty
(
$this
->
config
[
'user_roles'
]))
{
return
false
;
}
$auth_roles
=
array
();
foreach
(
$this
->
enumerate
(
true
)
as
$id
)
{
foreach
(
$this
->
config
[
'user_roles'
]
as
$prefix
=>
$role
)
{
if
(
strpos
(
$id
,
$prefix
)
===
0
)
{
$auth_roles
[]
=
$role
;
}
}
}
$role_attr
=
$this
->
config
[
'fieldmap'
][
'roles'
]
?:
'nsroledn'
;
if
(
$user_attrs
=
$this
->
conn
->
get_entry
(
$this
->
userdn
,
array
(
$role_attr
)))
{
$internals
=
array_values
(
$this
->
config
[
'user_roles'
]);
$new_attrs
=
$old_attrs
=
Net_LDAP3
::
normalize_entry
(
$user_attrs
);
$new_attrs
[
$role_attr
]
=
array_merge
(
array_unique
(
$auth_roles
),
array_filter
((
array
)
$old_attrs
[
$role_attr
],
function
(
$f
)
use
(
$internals
)
{
return
!
in_array
(
$f
,
$internals
);
})
);
$result
=
$this
->
conn
->
modify_entry
(
$this
->
userdn
,
$old_attrs
,
$new_attrs
);
return
!
empty
(
$result
);
}
return
false
;
}
/**
* Fetches user data from LDAP addressbook
*/
protected
function
get_ldap_record
(
$user
,
$key
)
{
$entry_dn
=
$this
->
get_entry_dn
(
$user
,
$key
);
if
(!
isset
(
$this
->
ldapcache
[
$entry_dn
]))
{
$this
->
ldapcache
[
$entry_dn
]
=
array
();
if
(
$this
->
ready
&&
(
$entry
=
$this
->
conn
->
get_entry
(
$entry_dn
,
array_values
(
$this
->
config
[
'fieldmap'
]))))
{
$this
->
ldapcache
[
$entry_dn
]
=
$this
->
field_mapping
(
$entry_dn
,
Net_LDAP3
::
normalize_entry
(
$entry
,
true
));
}
}
return
$this
->
ldapcache
[
$entry_dn
];
}
/**
* Compose a full DN for the given record identifier
*/
protected
function
get_entry_dn
(
$user
,
$key
)
{
$base_dn
=
$this
->
parse_vars
(
$this
->
config
[
'base_dn'
],
$key
);
return
sprintf
(
'%s=%s,%s'
,
$this
->
config
[
'rdn'
],
Net_LDAP3
::
quote_string
(
$key
,
true
),
$base_dn
);
}
/**
* Maps LDAP attributes to defined fields
*/
protected
function
field_mapping
(
$dn
,
$entry
)
{
$entry
[
'_dn'
]
=
$dn
;
$entry
[
'_raw'
]
=
$entry
;
// fields mapping
foreach
(
$this
->
config
[
'fieldmap'
]
as
$field
=>
$attr
)
{
$attr_lc
=
strtolower
(
$attr
);
if
(
isset
(
$entry
[
$attr_lc
]))
{
$entry
[
$field
]
=
$this
->
value_mapping
(
$field
,
$entry
[
$attr_lc
],
true
);
}
else
if
(
isset
(
$entry
[
$attr
]))
{
$entry
[
$field
]
=
$this
->
value_mapping
(
$field
,
$entry
[
$attr
],
true
);
}
}
return
$entry
;
}
/**
*
*/
protected
function
value_mapping
(
$attr
,
$value
,
$reverse
=
false
)
{
if
(
$map
=
$this
->
config
[
'valuemap'
][
$attr
])
{
if
(
$reverse
)
{
$map
=
array_flip
(
$map
);
}
if
(
is_array
(
$value
))
{
$value
=
array_filter
(
array_map
(
function
(
$val
)
use
(
$map
)
{
return
$map
[
$val
];
},
$value
));
}
else
{
$value
=
$map
[
$value
];
}
}
// convert (date) type
switch
(
$this
->
config
[
'attrtypes'
][
$attr
])
{
case
'datetime'
:
$ts
=
is_numeric
(
$value
)
?
$value
:
strtotime
(
$value
);
if
(
$ts
)
{
$value
=
gmdate
(
$reverse
?
'U'
:
'YmdHi
\Z
'
,
$ts
);
}
break
;
case
'integer'
:
$value
=
intval
(
$value
);
break
;
}
return
$value
;
}
/**
* Prepares filter query for LDAP search
*/
protected
function
parse_vars
(
$str
,
$key
)
{
$user
=
$this
->
username
;
if
(
strpos
(
$user
,
'@'
)
>
0
)
{
list
(
$u
,
$d
)
=
explode
(
'@'
,
$user
);
}
else
if
(
$this
->
userdn
)
{
$u
=
$this
->
userdn
;
$d
=
trim
(
str_replace
(
',dc='
,
'.'
,
substr
(
$u
,
strpos
(
$u
,
',dc='
))),
'.'
);
}
if
(
$this
->
userdn
)
{
$user
=
$this
->
userdn
;
}
// build hierarchal domain string
$dc
=
$this
->
conn
->
domain_root_dn
(
$d
);
$class
=
$this
->
config
[
'classmap'
]
?
$this
->
config
[
'classmap'
][
'*'
]
:
'*'
;
// map key to objectclass
if
(
is_array
(
$this
->
config
[
'classmap'
]))
{
foreach
(
$this
->
config
[
'classmap'
]
as
$k
=>
$c
)
{
if
(
strpos
(
$key
,
$k
)
===
0
)
{
$class
=
$c
;
break
;
}
}
}
$replaces
=
array
(
'%dc'
=>
$dc
,
'%d'
=>
$d
,
'%fu'
=>
$user
,
'%u'
=>
$u
,
'%c'
=>
$class
);
return
strtr
(
$str
,
$replaces
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Mon, Apr 6, 1:16 AM (1 d, 13 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
92/61/2cc8ee0b1e44d3cef9a63538a4ea
Default Alt Text
LDAP.php (10 KB)
Attached To
Mode
rRPK roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline