Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
t3o
my.typo3.org
Commits
6601b013
Commit
6601b013
authored
Dec 14, 2018
by
Thomas Löffler
Browse files
Add 'extensions/t3o_ldap/' from commit '
dbaaae85
'
git-subtree-dir: extensions/t3o_ldap git-subtree-mainline:
c37ac5ef
git-subtree-split:
dbaaae85
parents
c37ac5ef
dbaaae85
Changes
12
Hide whitespace changes
Inline
Side-by-side
extensions/t3o_ldap/Classes/Connectors/Ldap.php
0 → 100644
View file @
6601b013
<?php
namespace
T3o\T3oLdap\Connectors
;
/*
* (c) 2016 by mehrwert intermediale kommunikation GmbH
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use
TYPO3\CMS\Core\Utility\GeneralUtility
;
use
TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility
;
/**
* LDAP connector class to update accounts and passwords for LDAP user
* identified by DN. Passwords may be a multivalue attribute hashed by
* mechanisms defined in the PasswordHashing class. Currently CRYPT,
* SHA1 and MD5 are used.
*
* @package Typo3\Ldap\Connectors
* @since 1.0.0
*/
class
Ldap
{
/**
* LDAP server as IP, hostname or complete URI
*
* @var string
*/
private
$ldapServer
=
''
;
/**
* LDAP server port (0/389/636)
*
* @var int
*/
private
$ldapServerPort
=
0
;
/**
* LDAP version (defaults sto 3)
*
* @var int
*/
private
$ldapProtocolVersion
=
3
;
/**
* LDAP admin DN to bind for directory updates
*
* @var string
*/
private
$ldapBindDn
=
''
;
/**
* Bind password for administrative LDAP bind
*
* @var string
*/
private
$ldapBindPassword
=
''
;
/**
* LDAP connection resource
*
* @var null
*/
private
$ldapConnection
=
null
;
/**
* LDAP base DN used to find users in LDAP. May be overridden in
* extension manager configuration
*
* @var string
*/
private
$ldapBaseDnForPasswordChanges
=
'ou=people,dc=typo3,dc=org'
;
/**
* TYPO3 extension configuration array
*
* @var array
*/
private
$extensionConfiguration
=
[];
/**
* Last LDAP error in this class
*
* @var string
*/
private
$lastLdapError
=
''
;
/**
* LDAP constructor.
*/
public
function
__construct
()
{
// Disable certificate checks on LDAP TLS
putenv
(
'LDAPTLS_REQCERT=never'
);
// TODO Move to TypoScript configuration object if more than one LDAP server is required per installation
$this
->
extensionConfiguration
=
unserialize
(
$GLOBALS
[
'TYPO3_CONF_VARS'
][
'EXT'
][
'extConf'
][
't3o_ldap'
]);
$this
->
ldapServer
=
trim
(
$this
->
extensionConfiguration
[
'ldapServer'
]);
$this
->
ldapServerPort
=
intval
(
$this
->
extensionConfiguration
[
'ldapServerPort'
]);
$this
->
ldapProtocolVersion
=
intval
(
$this
->
extensionConfiguration
[
'ldapProtocolVersion'
]);
$this
->
ldapBindDn
=
trim
(
$this
->
extensionConfiguration
[
'ldapBindDn'
]);
$this
->
ldapBindPassword
=
$this
->
extensionConfiguration
[
'ldapBindPassword'
];
$this
->
ldapBaseDnForPasswordChanges
=
trim
(
$this
->
extensionConfiguration
[
'ldapBaseDnForPasswordChanges'
]);
// Connect and bind
$this
->
createLdapConnection
();
$this
->
ldapBind
(
$this
->
ldapConnection
,
$this
->
ldapBindDn
,
$this
->
ldapBindPassword
);
}
/**
* Test a LDAP bind using given dn and password. Returns true on success
* and false on bind failure. Errors are logged to syslog.
*
* @param string $dn Complete bind DN
* @param string $password Password to bind with
* @return bool
*/
public
function
testLdapPassword
(
$dn
,
$password
)
{
$ret
=
false
;
if
(
$this
->
createLdapConnection
()
===
true
)
{
if
(
$this
->
ldapBind
(
$this
->
ldapConnection
,
$dn
,
$password
)
===
true
)
{
$ret
=
true
;
}
}
else
{
GeneralUtility
::
sysLog
(
'Keine LDAP-Bind mit Nutzerdaten moeglich: '
.
ldap_error
(
$this
->
ldapConnection
),
't3o_ldap'
,
GeneralUtility
::
SYSLOG_SEVERITY_ERROR
);
}
return
$ret
;
}
/**
* Create LDAP connection and bind with admin credentials. Update passwords for
* a given user (by $username) for all available mechanisms. Errors are logged
* to syslog.
*
* @param string $username Username for bind
* @param array $values The password array
* @return bool
*/
public
function
setLdapPasswords
(
$username
,
$values
)
{
$ret
=
false
;
// Create LDAP connection
if
(
$this
->
createLdapConnection
()
===
true
)
{
// Try to bind as admin
if
(
$this
->
ldapBind
(
$this
->
ldapConnection
,
$this
->
ldapBindDn
,
$this
->
ldapBindPassword
)
===
true
)
{
$dn
=
$this
->
getDnForUserName
(
$username
);
// TODO Check if user exists and create if not exists?
// Finally try to update passwords
$result
=
$this
->
updateLdapAttribute
(
$dn
,
'userPassword'
,
$values
,
true
);
if
(
$result
===
false
)
{
GeneralUtility
::
sysLog
(
ldap_error
(
$this
->
ldapConnection
),
't3o_ldap'
,
GeneralUtility
::
SYSLOG_SEVERITY_ERROR
);
}
}
else
{
GeneralUtility
::
sysLog
(
'Unable to bind to LDAP using: '
.
ldap_error
(
$this
->
ldapConnection
),
't3o_ldap'
,
GeneralUtility
::
SYSLOG_SEVERITY_ERROR
);
}
}
else
{
GeneralUtility
::
sysLog
(
'No active LDAP connection available'
,
't3o_ldap'
,
GeneralUtility
::
SYSLOG_SEVERITY_ERROR
);
}
return
$ret
;
}
/**
* Bind to the LDAP directory with the given credentials. Errors are logged to syslog.
*
* @param resource $ldapConnection
* @param String $dn Complete bind DN for LDAP entry to bind with
* @param String $password The password to use for bind
* @return bool
*/
private
function
ldapBind
(
$ldapConnection
,
$dn
,
$password
)
{
$ret
=
false
;
try
{
// Bind to LDAP server
$ldapBind
=
@
ldap_bind
(
$ldapConnection
,
$dn
,
$password
);
// Verify binding
if
(
$ldapBind
)
{
$ret
=
true
;
}
else
{
throw
new
\
RuntimeException
(
'Could not bind to LDAP connection: '
.
ldap_error
(
$ldapConnection
),
1453993540
);
}
}
catch
(
\
RuntimeException
$e
)
{
GeneralUtility
::
sysLog
(
$e
->
getMessage
(),
't3o_ldap'
,
GeneralUtility
::
SYSLOG_SEVERITY_ERROR
);
}
return
$ret
;
}
/**
* Update an attribute for the given DN. Errors are logged to syslog.
*
* @param String $dn Complete DN for LDAP entry to update attributes for
* @param string $attribute The name of the attribute
* @param string|array $attributeValues String or array (for multivalue attributes)
* @param bool $multiValue Whether or not the attribute should be treated as single or multivalue
* @return bool
*/
private
function
updateLdapAttribute
(
$dn
,
$attribute
,
$attributeValues
,
$multiValue
=
false
)
{
$ret
=
false
;
if
(
trim
(
$dn
)
!==
''
)
{
$attributes
=
[];
if
(
is_array
(
$attributeValues
))
{
foreach
(
$attributeValues
AS
$attributeValue
)
{
$attributes
[
$attribute
][]
=
$attributeValue
;
}
}
else
{
$attributes
[
$attribute
]
=
$attributeValues
;
}
$ret
=
ldap_mod_replace
(
$this
->
ldapConnection
,
trim
(
$dn
),
$attributes
);
}
return
$ret
;
}
/**
* Create the LDAP connection and set in global scope on success. Return false on failure.
* Errors are logged to syslog.
*
* @return bool
*/
private
function
createLdapConnection
()
{
$ret
=
false
;
$port
=
intval
(
$this
->
ldapServerPort
);
try
{
$this
->
ldapConnection
=
@
ldap_connect
(
$this
->
ldapServer
,
(
$port
>
0
?
$port
:
null
));
if
(
$this
->
ldapConnection
)
{
// Set protocol version
if
(
ldap_set_option
(
$this
->
ldapConnection
,
LDAP_OPT_PROTOCOL_VERSION
,
$this
->
ldapProtocolVersion
))
{
if
(
ldap_set_option
(
$this
->
ldapConnection
,
LDAP_OPT_REFERRALS
,
0
))
{
$ret
=
true
;
}
}
}
else
{
throw
new
\
RuntimeException
(
'Could not create LDAP connection: '
.
ldap_error
(
$this
->
ldapConnection
),
1453993539
);
}
}
catch
(
\
RuntimeException
$e
)
{
GeneralUtility
::
sysLog
(
$e
->
getMessage
(),
't3o_ldap'
,
GeneralUtility
::
SYSLOG_SEVERITY_ERROR
);
}
return
$ret
;
}
/**
* Wrap with base DN to provide a valid DN to identify the user in the
* directory service.
*
* @param string $username The username to wrap with the base DN
* @return string
*/
private
function
getDnForUserName
(
$username
)
{
$dn
=
'uid='
.
$username
.
','
.
$this
->
ldapBaseDnForPasswordChanges
;
return
$dn
;
}
/**
* Check if a user exists in LDAP
*
* @param String $username The username
* @return bool
*/
public
function
userExists
(
$username
)
{
$ret
=
false
;
$dn
=
$this
->
getDnForUserName
(
$username
);
$filter
=
'(|(objectClass=typo3Person))'
;
$attributes
=
[
'sn'
,
'email'
,
'ou'
];
$searchResult
=
@
ldap_search
(
$this
->
ldapConnection
,
$dn
,
$filter
,
$attributes
);
if
(
$searchResult
)
{
$info
=
ldap_get_entries
(
$this
->
ldapConnection
,
$searchResult
);
if
(
intval
(
$info
[
'count'
])
>
0
)
{
$ret
=
true
;
}
}
return
$ret
;
}
/**
* Update a user in LDAP
*
* @param \In2code\Femanager\Domain\Model\User $user The user data array
* @return bool
*/
public
function
updateUser
(
\
In2code\Femanager\Domain\Model\User
$user
)
{
$ret
=
false
;
$dn
=
$this
->
getDnForUserName
(
$user
->
getUsername
());
$ldapUserObject
=
$this
->
buildLdapUserArray
(
$user
);
$res
=
ldap_modify
(
$this
->
ldapConnection
,
$dn
,
$ldapUserObject
);
if
(
$res
===
true
)
{
// TODO $this->updateFeUserLastLdapUpdateTimestamp($feUserUid);
$ret
=
true
;
}
else
{
$this
->
setLastLdapError
(
ldap_error
(
$this
->
ldapConnection
));
}
return
$ret
;
}
/**
* Enable a user in LDAP
*
* @param string $username Username for DN
* @return bool
* @todo Requires refactoring for proper syntax of LDAP enabled/disabled
*/
public
function
enableUser
(
$username
)
{
$ret
=
false
;
$dn
=
$this
->
getDnForUserName
(
$username
);
$ldapUserObject
=
[
'active'
=>
true
];
$res
=
ldap_modify
(
$this
->
ldapConnection
,
$dn
,
$ldapUserObject
);
if
(
$res
===
true
)
{
// TODO $this->updateFeUserLastLdapUpdateTimestamp($feUserUid);
$ret
=
true
;
}
else
{
$this
->
setLastLdapError
(
ldap_error
(
$this
->
ldapConnection
));
}
return
$ret
;
}
/**
* Delete a user in LDAP
*
* @param string $username The username to delete in LDAP
* @return bool
*/
public
function
deleteUser
(
$username
)
{
$dn
=
$this
->
getDnForUserName
(
$username
);
return
ldap_delete
(
$this
->
ldapConnection
,
$dn
);
}
/**
* Create a user in LDAP
*
* @param \In2code\Femanager\Domain\Model\User $user The user model
* @param string $password Clear text user password
* @return bool
*/
public
function
createUser
(
\
In2code\Femanager\Domain\Model\User
$user
,
$password
=
''
)
{
$ret
=
false
;
$dn
=
$this
->
getDnForUserName
(
$user
->
getUsername
());
$ldapUserObject
=
$this
->
buildLdapUserArray
(
$user
);
$res
=
ldap_add
(
$this
->
ldapConnection
,
$dn
,
$ldapUserObject
);
if
(
$res
===
true
)
{
$this
->
updateFeUserLastLdapUpdateTimestamp
(
$user
->
getUid
());
$ret
=
true
;
}
else
{
$this
->
setLastLdapError
(
ldap_error
(
$this
->
ldapConnection
));
}
return
$ret
;
}
/**
* Build the array for LDAP insert or updates.
*
* @param \In2code\Femanager\Domain\Model\User $user
* @return array
*/
private
function
buildLdapUserArray
(
\
In2code\Femanager\Domain\Model\User
$user
)
{
$ldapUserArray
=
[
'objectclass'
=>
[
0
=>
'top'
,
1
=>
'person'
,
2
=>
'typo3Person'
,
3
=>
'inetOrgPerson'
]
];
$nameParts
=
GeneralUtility
::
trimExplode
(
' '
,
$user
->
getName
());
$lastName
=
array_pop
(
$nameParts
);
$firstName
=
implode
(
' '
,
$nameParts
);
if
(
trim
(
$user
->
getName
())
!==
''
)
{
$ldapUserArray
[
'cn'
]
=
$user
->
getName
();
}
if
(
trim
(
$user
->
getName
())
!==
''
)
{
$ldapUserArray
[
'displayName'
]
=
trim
(
$user
->
getName
());
}
if
(
trim
(
$firstName
)
!==
''
)
{
$ldapUserArray
[
'givenName'
]
=
trim
(
$firstName
);
}
if
(
trim
(
$lastName
)
!==
''
)
{
$ldapUserArray
[
'sn'
]
=
trim
(
$lastName
);
}
if
(
trim
(
$user
->
getAddress
())
!==
''
)
{
$ldapUserArray
[
'street'
]
=
trim
(
$user
->
getAddress
());
}
if
(
trim
(
$user
->
getZip
())
!==
''
)
{
$ldapUserArray
[
'postalCode'
]
=
trim
(
$user
->
getZip
());
}
if
(
trim
(
$user
->
getCity
())
!==
''
)
{
$ldapUserArray
[
'l'
]
=
trim
(
$user
->
getCity
());
}
if
(
trim
(
$user
->
getCountry
())
!==
''
)
{
$countryDetails
=
$this
->
getCountryDetailsByCountryName
(
$user
->
getCountry
());
if
(
$countryDetails
!==
false
)
{
if
(
trim
(
$countryDetails
[
'cn_iso_2'
])
!==
''
)
{
$ldapUserArray
[
'c'
]
=
trim
(
$countryDetails
[
'cn_iso_2'
]);
}
if
(
trim
(
$countryDetails
[
'cn_short_en'
])
!==
''
)
{
$ldapUserArray
[
'co'
]
=
trim
(
$countryDetails
[
'cn_short_en'
]);
}
}
}
$url
=
filter_var
(
$user
->
getWww
(),
FILTER_VALIDATE_URL
);
if
(
$url
!==
false
)
{
$ldapUserArray
[
'labeledURI'
]
=
$url
;
}
$email
=
filter_var
(
$user
->
getEmail
(),
FILTER_VALIDATE_EMAIL
);
if
(
$email
!==
false
)
{
$ldapUserArray
[
'mail'
]
=
$email
;
}
if
(
trim
(
$user
->
getTelephone
())
!==
''
)
{
$ldapUserArray
[
'homePhone'
]
=
trim
(
$user
->
getTelephone
());
}
if
(
trim
(
$user
->
getFax
())
!==
''
)
{
$ldapUserArray
[
'facsimileTelephoneNumber'
]
=
trim
(
$user
->
getFax
());
}
// update terms and conditions data
if
(
$user
->
isTerms
()
===
true
)
{
$ldapUserArray
[
'conditionsAccepted'
]
=
1
;
}
if
(
$user
->
getTermsDateOfAcceptance
()
!==
null
&&
$user
->
getTermsDateOfAcceptance
()
->
getTimestamp
()
>
0
)
{
$ldapUserArray
[
'conditionsDate'
]
=
$user
->
getTermsDateOfAcceptance
()
->
getTimestamp
();
}
$objectManager
=
GeneralUtility
::
makeInstance
(
\
TYPO3\CMS\Extbase\Object\ObjectManager
::
class
);
$myProfileRepository
=
$objectManager
->
get
(
\
T3o\T3omy\Domain\Repository\MyProfileRepository
::
class
);
/** @var \T3o\T3omy\Domain\Model\MyProfile $myProfileUser */
$myProfileUser
=
$myProfileRepository
->
findByUid
(
$user
->
getUid
());
if
(
$myProfileUser
===
null
)
{
// if no user is found, we try to find disabled users. This is needed, if we confirm new users via backend
$myProfileUser
=
$myProfileRepository
->
findDisabledByUid
(
$user
->
getUid
());
}
if
(
$myProfileUser
->
getTermsVersion
()
!==
''
)
{
$ldapUserArray
[
'conditionsVersion'
]
=
$myProfileUser
->
getTermsVersion
();
}
// If the password is not salted, it has been submitted and must be included in the LDAP update
if
(
$this
->
isSaltedPassword
(
$user
->
getPassword
())
===
false
)
{
/** @var \T3o\T3oLdap\Utility\PasswordHashing $passwordHashing */
$passwordHashing
=
GeneralUtility
::
makeInstance
(
\
T3o\T3oLdap\Utility\PasswordHashing
::
class
);
$ldapUserArray
[
'userPassword'
][]
=
$passwordHashing
->
getPasswordHash
(
$user
->
getPassword
(),
'sha1'
);
$ldapUserArray
[
'userPassword'
][]
=
$passwordHashing
->
getPasswordHash
(
$user
->
getPassword
(),
'crypt'
);
$ldapUserArray
[
'userPassword'
][]
=
$passwordHashing
->
getPasswordHash
(
$user
->
getPassword
(),
'md5'
);
}
// if hash fields are filled, store them into ldap user and remove them afterwards
if
(
$this
->
isSaltedPassword
(
$user
->
getPassword
())
&&
(
$myProfileUser
->
getHashMd5
()
||
$myProfileUser
->
getHashSha1
()
||
$myProfileUser
->
getHashCrypt
()))
{
$ldapUserArray
[
'userPassword'
][]
=
$myProfileUser
->
getHashSha1
();
$ldapUserArray
[
'userPassword'
][]
=
$myProfileUser
->
getHashCrypt
();
$ldapUserArray
[
'userPassword'
][]
=
$myProfileUser
->
getHashMd5
();
$myProfileUser
->
setHashCrypt
(
''
);
$myProfileUser
->
setHashMd5
(
''
);
$myProfileUser
->
setHashSha1
(
''
);
$myProfileRepository
->
update
(
$myProfileUser
);
GeneralUtility
::
makeInstance
(
\
TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager
::
class
)
->
persistAll
();
}
if
(
trim
(
$ldapUserArray
[
'sn'
])
===
''
)
{
$ldapUserArray
[
'sn'
]
=
$user
->
getUsername
();
}
if
(
trim
(
$ldapUserArray
[
'cn'
])
===
''
)
{
$ldapUserArray
[
'cn'
]
=
$user
->
getUsername
();
}
if
(
trim
(
$ldapUserArray
[
'uid'
])
===
''
)
{
$ldapUserArray
[
'uid'
]
=
$user
->
getUsername
();
}
return
$ldapUserArray
;
}
/**
* Check a given String for salting.
*
* @param String $passwordString The password string
* @return bool
*/
private
function
isSaltedPassword
(
$passwordString
)
{
$ret
=
false
;
if
(
$passwordString
!==
''
)
{
if
(
SaltedPasswordsUtility
::
isUsageEnabled
(
'FE'
))
{
$objSalt
=
GeneralUtility
::
makeInstance
(
SaltedPasswordsUtility
::
getDefaultSaltingHashingMethod
(
'FE'
));
if
(
is_object
(
$objSalt
))
{
if
(
$objSalt
->
isValidSaltedPW
(
$passwordString
))
{
$ret
=
true
;
}
}
}
}
return
$ret
;
}
/**
* Update the last modified in LDAP timestamp of a user
*
* @todo Rebuild it with Doctrine DBAL
* @param $feUserUid
* @return mixed
*/
private
function
updateFeUserLastLdapUpdateTimestamp
(
$feUserUid
)
{
return
$GLOBALS
[
'TYPO3_DB'
]
->
exec_UPDATEquery
(
'fe_users'
,
'uid = '
.
intval
(
$feUserUid
),
[
'tx_t3oldap_lastupdate_ts'
=>
$GLOBALS
[
'EXEC_TIME'
]
]
);
}
/**
* @return string
*/
public
function
getLastLdapError
()
{
return
$this
->
lastLdapError
;
}
/**
* @param string $lastLdapError
* @return void
*/
public
function
setLastLdapError
(
$lastLdapError
)
{
$this
->
lastLdapError
=
$lastLdapError
;
}
/**
* @todo Rebuild it with Doctrine DBAL
* @param $countryName
* @return bool
*/
private
function
getCountryDetailsByCountryName
(
$countryName
)
{
$ret
=
false
;
$whereClause
=
'cn_short_en LIKE '
.
$GLOBALS
[
'TYPO3_DB'
]
->
fullQuoteStr
(
$countryName
,
'static_countries'
);
$selectFields
=
'uid, cn_iso_2, cn_short_en'
;
$fromTable
=
'static_countries'
;
$groupBy
=
''
;
$orderBy
=
''
;
$limit
=
'1'
;
$result
=
$GLOBALS
[
'TYPO3_DB'
]
->
exec_SELECTquery
(
$selectFields
,
$fromTable
,
$whereClause
,
$groupBy
,
$orderBy
,
$limit
);
if
(
$result
)
{
if
(
$GLOBALS
[
'TYPO3_DB'
]
->
sql_num_rows
(
$result
)
==
1
)
{
$ret
=
$GLOBALS
[
'TYPO3_DB'
]
->
sql_fetch_assoc
(
$result
);