-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rework the user ldap logic to match the account table #125
Conversation
1b62dad
to
fe52c0d
Compare
0d6ff75
to
8d2057e
Compare
|
To me, it is cleaner version of the code + adjustment to accounts thing. Looks good in general, just difficult to review 👍 |
Tested with master:
|
* creates a unique name for internal ownCloud use. | ||
* @param string $name the display name of the object | ||
* @param boolean $isUser whether name should be created for a user (true) or a group (false) | ||
* @return string|false with with the name to use in ownCloud or false if unsuccessful | ||
*/ | ||
private function createAltInternalOwnCloudName($name, $isUser) { | ||
public function createAltInternalOwnCloudName($name, $isUser) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some more context here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll also need context here.
If this needs to be public I wonder if this function should be here. It would make more sense to move this function to a specialized object.
Although we should move this function to another place, I think it's fine if we can make it private for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
separate PR, see lib\User\Manager.php:292:
// FIXME move to a better place, naming related. eg DistinguishedNameUtils
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still need to review the big diffs
@@ -82,7 +78,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { | |||
$uid = $input->getArgument('ocName'); | |||
$this->isAllowed($input->getOption('force')); | |||
$this->confirmUserIsMapped($uid); | |||
$exists = $this->backend->userExistsOnLDAP($uid); | |||
$exists = $this->backend->userExists($uid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reason for this change? I think originally this was intended to bypass the cache and check directly against the LDAP server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation now always talks to LDAP but caches the result per request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually userExistsOnLdap has been moved to User_LDAP. userExists tries to determine the answer based on the cache in the LDAP UserManager. It will fall back to LDAP just as before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so the redis cache has been removed and we cache the information per request... fair enough 👍
$username = $this->getAttributeValue($attr, null); | ||
} | ||
if ($username === null) { | ||
$json = @json_encode($this->ldapEntry, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_NUMERIC_CHECK | JSON_PARTIAL_OUTPUT_ON_ERROR ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather have a comment here for the @
since error supression isn't common.
/** | ||
* @param string $ownCloudUID | ||
*/ | ||
public function setOwnCloudUID($ownCloudUID) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried this is the only "set" method here. Is it expected to be changed? Could it be set during the object creation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to store an already mapped uid somewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point is if we can set the value only in the constructor in order to prevent modifications, or we need to modify the value once the object is already constructor.
lib/User/UserEntry.php
Outdated
} | ||
} | ||
if ($quota === null) { | ||
$quota = 'default'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this changed recently to not set the "default" quota in order to avoid being overwritten
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix incoming
lib/User/UserEntry.php
Outdated
} | ||
|
||
/** | ||
* @return string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd comment here what is the expected behavior. I don't expect this method to return the username unless explicitly told.
My expectation is that this method returns null if the displayname can't be fetched, so anyone can fetch the username later if they want. Better to add a comment if this isn't the case
lib/User/UserEntry.php
Outdated
/** | ||
* returns the home directory of the user if specified by LDAP settings | ||
* @return string|null | ||
* @throws \Exception |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment here when this exception is expected to be thrown.
In addition, we should narrow the exception.
if (isset($this->ldapEntry['memberof'])) { | ||
return $this->ldapEntry['memberof']; | ||
} | ||
return []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather return something different because empty result is also a valid result, it would be difficult to distinguish this case and an error case (or if there isn't such entry)
lib/User/UserEntry.php
Outdated
*/ | ||
private function getAttributeValue($attributeName, $default = null, $trim = true) { | ||
$attributeName = strtolower($attributeName); // all ldap keys are lowercase | ||
if (isset($this->ldapEntry[$attributeName][0])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we check for multivalued attributes? We should have a comment just in case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no. only search attributes may be multi value. updating phpdoc as well
lib/User/UserEntry.php
Outdated
* @param $attributeName string | ||
* @param $default string | ||
* @param $trim bool don't trim value, eg for binary data | ||
* @return string|null|mixed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specify when we should expect each data type.
use OCP\UserInterface; | ||
|
||
class User_Proxy extends Proxy implements IUserBackend, UserInterface, IProvidesExtendedSearchBackend { | ||
class User_Proxy extends Proxy implements IUserBackend, UserInterface, IProvidesQuotaBackend, IProvidesExtendedSearchBackend, IProvidesEMailBackend { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm forced to complain about the massive interface implementation here, but no change needed for now because it will require quite huge changes, and not just here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The app used to update core itself, which is not the way it shoud be. This PR just corrects that by removing the related code and implementing the interfaces to get the correct behavior.
@@ -25,6 +26,9 @@ | |||
namespace OCA\User_LDAP; | |||
|
|||
abstract class LDAPUtility { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should review the usage if this class because right now I think it's quite pointless. Probably we can remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
separate PR
lib/Access.php
Outdated
* returns the Connection instance | ||
* @return \OCA\User_LDAP\ILDAPWrapper | ||
*/ | ||
public function getLDAP() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this function used outside of the class? I'm fine to use this function as a shortcut but also private. Making it public would open new dependencies between components which doesn't seem a good idea.
$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20); | ||
|
||
return strtoupper($hex_guid_to_guid_str); | ||
public static function binGUID2str($binGuid) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to use static methods here? I'd rather move this function to a helper object and inject it in the constructor. This way we have this function outside if this class (I don't think this is needed to be here), and also make the function testeable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
separate PR
@@ -1604,7 +1679,7 @@ public function getSID($dn) { | |||
* @param string $sid | |||
* @return string | |||
*/ | |||
public function convertSID2Str($sid) { | |||
public static function sid2str($sid) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
separate PR
* The nsuniqueid values are generated based on the entryuuid value by moving the "-" to comply with the format of the ODSEE Nsuniqueid Virtual Attribute attribute. | ||
* | ||
* ## RedHat FreeIPA is defined as utf string | ||
* {@link https://github.com/freeipa/freeipa/blob/master/install/share/uuid.ldif ipaUniqueID schema} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
Really greatful for this references. Just this one should be an official redhat link. No need to do this now anyway.
lib/User_LDAP.php
Outdated
* | ||
* @package OCA\User_LDAP | ||
*/ | ||
class User_LDAP implements IUserBackend, UserInterface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this class should implement these interfaces. It should be oriented to implement them so the User_Proxy class is easier to implement but other than that, the interfaces will be implemented by the User_Proxy.
lib/User_LDAP.php
Outdated
$user = $this->access->userManager->get($uid); | ||
if(!$user instanceof User) { | ||
$user = $this->userManager->getByOwnCloudUID($uid); | ||
if(!$user instanceof UserEntry) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the PHPDocs, the method returns a UserEntry or null. I'd change this to check against null because otherwise it seems that other type of objects can be returned by that function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getByOwnCloudUID now always returns a UserEntry
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or throws an exception
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so we can remove that check (or use a try-catch for the exception instead)
* Get a users email address | ||
* | ||
* @param string $uid The username | ||
* @return string|null|false false if user was not found, null if no email is set |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copy & paste 👀
lib/User_LDAP.php
Outdated
* FIXME This is an expensive operation and takes roughly half a second to parse the data and create the image. This might be too slow for sync jobs. | ||
* | ||
* @param string $uid The username | ||
* @return IImage|null|false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to specify each possible cases
lib/User_LDAP.php
Outdated
/** | ||
* Get avatar for a users account for core powered user search | ||
* | ||
* FIXME This is an expensive operation and takes roughly half a second to parse the data and create the image. This might be too slow for sync jobs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side question for the future: is it possible to store the raw image data (I think it's base64 encoded) and compare the data we have and the data we receive, and then create the image if the data is different? I'm not sure if it will improve the performance, but it might be worthy to have a try.
Review finished from my part. I think most of them are easy to solve, and the rest might be moved to tech debt if they're too complex to manage in this PR. |
bug here with user sync, suggested arribute is not found when it is present: https://github.com/owncloud/enterprise/issues/2275 |
76dccbe
to
602c16e
Compare
Rebased to include some other fixes and the tests should now run |
"0" quota is recorded in the accounts table as NULL => correct or not? |
lib/User_LDAP.php
Outdated
if($userEntry === null) { | ||
try { | ||
$this->userManager->getCachedEntry($uid); | ||
} catch (\Exception $e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
This mornings commit fixes the sync issue I found. |
…to getCachedEntry
…ption to trigger the fetching from ldap
…etrieve a results which was not cached and return null.
…w takes array of attributes
3ddbc28
to
c9ed34f
Compare
Rebased + pushed a test. Will wait for CI. Then build test tarball which we can share round as a beta release 🎉 |
Ideas for performance comparisons for release:
|
Merging, next up testing with large installations before release |
I am sick of our ldap problems. The app is heavily used, yet undertested and quirky at best. It grew over time but its purpose changes with the introduction of the Account table in OC10.
requires owncloud/core#28729 to make the user sync correctly update quota and email
yes, I am ripping this apart and putting it together anew. I don't care about the ui. I need the underlying sync logic to work reliably AND the code to be readable. That means not trying to be smart and use less reflection ...