Can I already use OCP\User\Backend to implement a user backend

I found the OCP\User\Backend namespace that contains Interfaces for a user backend actions. Is this production ready? I have only seen Databaseusing it and the itnerfaces seem to be stiill evaluated through implementsActions() in Abackend.

@rullzer AFAICS you commited most of the Interfaces, could you help me out? I want to upgrade my User Backend Sql Raw app to this interface. I need to know whether this is the designated future arhitecture.

Here are specific questions:

  1. Can i already register classes implementing these Interfaces (e.g. ICheckPasswordBackend) to OCP\IUserManager like so

    $context->getAppContainer()->get('OCP\IUserManager')
    
  2. What exactly is IPasswordConfirmationBackend for?

  3. Is there an interface for deleting users?

  4. Searching for users using a pattern?

  5. Checking whether a user exists?

  6. Setting a home?

  7. Setting/getting e-mail address?

I would be happy to discuss this with you in more detail.

After searching through the code AFAICS no class uses the Interfaces for capability detection other than ABackend. I would be interested to know whether there is a road map for this and what it looks like.

Yes, the interfaces are the new way to do this. We just didn’t have the time to migrate the old code from the implementsActions mess.

I dug more into this as well:

  1. Has anyone already come up with a way to interpret the Interfaces as an alternative to implements Actions ?

  2. AFAIK interfaces do not allow runtime capability detection. When you define a backend you have to choose at development time whether you will be able to implement an action or not, e.g. whether you will be able to change a password. For my user backend app this is a problem because users might not provide SQL queries for certain actions and then the app has to figure out what it can do at runtime. There it would actually make sense to have a system that does not rely on implementing single-method interfaces.

    So while it may look like a cleaner solution, it also introduces a significant limitation.

You’re right, the implementation is kind of static. But only if you always register the same class or object. Can’t you register different implementations based on this specific logic? Like if condition X is met you register the one that implements a certain action, otherwise you register a class that doesn’t.

The slime one can be your base implementation, the other one can extend and amend the functionality.

AFAIK I could only do this using reflection and dynamically generate classes, couldn’t I? This does not seem to be “clean code” to me. But maybe i am missing something. I only program in PHP “as a hobby”.

Ah I’m not talking about this kind of code.

So you have to register your backend in some code like https://github.com/nextcloud/user_sql/blob/fc49c8ade754c93d1a169efb11e2febdff109667/lib/AppInfo/Application.php#L61.

You can have a condition around that like

if (...) {
  \OC::$server->getUserManager()->registerBackend($a);
} else {
  \OC::$server->getUserManager()->registerBackend($b);
}

where $a is the basic backend with a few actions implemented and $b is the one that has more/all actions.

Of course problems arise as soon as you want too many possible combinations of implemented interfaces. The decorator patterns comes to my mind but that does not help with static types as only the outer most object would have a certain interface.

I was talking about the generation of $a and $b. In my scenario there would be too many combinations to write them out during development. This is why, I assume that reflection (constructing classes during runtime) is the only solution.

My user backend app does not know which actions will be available to use. It allows users (or rather admins) to use raw SQL queries for e.g. getting usernames, getting their password hashes, writing passwird hashes etc. Since I don’t know which of these actions an admin will actually allow, i.e. provide an SQL query for, I need a dynamic system for that.

And I think this would make sense to many Nextcloud deployments, because there are always different setups where some user properties are stored here or there, some read-only, some writable.

I’ll try to come up with some pseudo code to demonstrate what I see as a better solution.

I see. What we could do is change the API so the individual methods of the actions can throw a NotSupportedException or similar, then you can bail out in certain cases.

Of course the API and handling in Nextcloud would have to be adjusted for this.

But also in all fairness I would argue that your back-end is very specific. The typical user back-end would statically know if it can do a certain action or not :wink:

Isn’t this a rather common case and decided on a instance-to-instance basis? How does the LDAP backend
deal with:

  • Company A uses LDAP to store usernames, passwords and user homes in LDAP and Nextcloud should ask it where to put files.

  • Company B only uses LDAP for usernames and passwords and lets Nextcloud store files wherever Nextcloud wants. It’s user backend does not offer getHome() to Nextcloud.

Both companies use the same LDAP backend app and therefore identical code.

You are right, there is some dynamic checking even in the LDAP app: https://github.com/nextcloud/server/blob/b76698c547bcaf22857f8ec67a8567399520f73a/apps/user_ldap/lib/User_LDAP.php#L524-L541

I started a new dev discussion about the API problems: User backend API overhaul.

1 Like