Skip to content
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

CustomUserBackend is loaded too late #5265

Open
stevie-sy opened this issue Sep 23, 2024 · 11 comments
Open

CustomUserBackend is loaded too late #5265

stevie-sy opened this issue Sep 23, 2024 · 11 comments
Labels
area/framework Affects third party integration/development bug Something isn't working
Milestone

Comments

@stevie-sy
Copy link

History

In the meantime, some time has passed as @nilmerg explained me how to create a own custom backend. The requirement was to call Icinga from an application portal (beside the possability via DbBackend) and use its authentication information for the login. Because it's a kind of external backend I should use as role model

class ExternalBackend implements UserBackendInterface
.
Now I have finally managed to do this, but I noticed a few things that I would like to describe here

Describe the bug

During the development I recognized that my module with the own backend is loaded too late and/or the authentifcation call was after putting the credentials into the LoginForm.
Depending on the situation icingaweb produces a lot of error messages like:
Can't create authentication backend "portal". An exception was thrown: <- Icinga\Exception\ConfigurationError in /usr/share/php/Icinga/Authentication/User/UserBackend.php with message: Authentication configuration for user backend "xxx" defines an invalid backend type. Backend type "xxx" is not supported
It doesn't matter in which order I configured my backend and the DbBackend at /etc/icingaweb2/authentication.ini. Because of that I had to debug the complete login sequence.

To Reproduce

The first problem (or actually hurdle) I encourtered is the function

protected function authExternal()
which is triggerd by
public function isAuthenticated()
. The problem what I saw is here, that the function authExternal checks if the backend is a instace of the class ExternalBackend. So If I create my own backend with the implemation of the Interface UserBackendInterface. The function returns false everytime and consequently the authentication fails. Here is the workarround for now that the own backend extends the class ExternalBackend although the most things isn't needed from that class. Maybe to create an own interface, which also will be checked in authExternal instead of the class name? Just to produce nicer code.

The second problem I encourtered is this call

foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $module) {
. All installed and activated modules are loaded alphabetically. For example I choose as name for my module "Mybackend". If there are many other modules loaded before like "audit", "icingadb" etc. it takes time, until the own module is loaded. And here happens a big problem: During loading every module and register possible custom backends, following function gets triggerd
protected function includeScript($file)
This function includes the configuration.php and excutes the existing PHP code. If there is a code like (from icingadb-web):

$auth = Auth::getInstance();
$authenticated = Icinga::app()->isWeb() && $auth->isAuthenticated();

the call will fail, because the own modul isn't loaded and the own backend isn't registred yet. This produces the mentioned error message above and happens as long as, until the own module gets loaded! Depending on the number of modules installed, the log file becomes filled with error messages!
The only workarround for this is to rename the own module to a little senseless name like "aaamybackend" to provoke the fact that the own module with the own backend is loaded first.

Expected behavior

I know this is a still rare case. Maybe not everybody is crazy like my/us 😆 to create a own backend. So get a solution for this will be low prio I think. For loading in a specific order instead of alphabetical could be to set something like a load order. So that, the call
Icinga::app()->getModuleManager()->getLoadedModules() returns the correct and needed order.
And for the "problem" with authExternal a solution could be an own interface as I wrote before. But that's only that the code will be nicer, not more.
I just wanted to draw attention to it.

Your Environment

Include as many relevant details about the environment you experienced the problem in

  • Icinga Web 2 version and modules (System - About): 2.12.1
  • Web browser used: Edge (128.0.2739.79) / Firefox (115.15.0esr)
  • Icinga 2 version used (icinga2 --version): 2.14.2-1
  • PHP version used (php --version): 8.0.30
  • Server operating system and version: RHEL 9.4
@nilmerg
Copy link
Member

nilmerg commented Sep 24, 2024

Hi Stevie ;)

Here is the workarround for now that the own backend extends the class ExternalBackend although the most things isn't needed from that class.

Yep. Would be my suggestion as well, though.

because the own modul isn't loaded and the own backend isn't registred yet.

Have you tried putting the registration of your own backend in run.php? It's run early on, earlier than configuration.php, and the supposed location for hooks (which a custom user backend is somewhat) in general.

@stevie-sy
Copy link
Author

Yes I tried run.php as well. Didn't work. But I changed my code so many times until it worked at this time. 😄 So it could be an other reason at this time. I will test this for you and report back 😉

@Wintermute2k6
Copy link
Contributor

ref/NC/830526

@stevie-sy
Copy link
Author

So testet it. And the result is the same like written above:
It doesn't matter if the call $this->provideUserBackend is put into run.php nor configuration.php. The own module which register the own backend must be loaded before every other module which will do some checks within Auth::getInstance(); e.g. icingadb-web is doing.

Until there is no load order, you can only choose a name for the module that will ensure that it ranks first in an alphabetical list like the result of this call Icinga::app()->getModuleManager()->getLoadedModules() in

foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $module) {

@nilmerg
Copy link
Member

nilmerg commented Oct 7, 2024

Works, for me. 😁

I've put this in run.php:

$this->provideUserBackend('test', MyExternalBackend::class);

Which refers to this class:

use Icinga\Authentication\User\ExternalBackend;
use Icinga\Data\ConfigObject;

class MyExternalBackend extends ExternalBackend
{
    public function __construct(ConfigObject $config)
    {

    }

    public static function getRemoteUser($variable = 'REMOTE_USER')
    {
        return 'icingaadmin';
    }
}

And last but not least, an entry in authentication.ini:

[test]
backend = "test"

Now everyone accessing my Web is an admin 🎉

@stevie-sy
Copy link
Author

stevie-sy commented Oct 14, 2024

Hmm... intersting ..

Is there a diffrence between:

  • $this->provideUserBackend('test', MyExternalBackend::class);
  • $this->provideUserBackend('portal','Icinga\\Module\\MyExternalBackend\\MyExternalBackendClass');

The last variant you told me month ago ...

Back from vacation I will test it and give feedback

BTW: I know this topic is more then entry level. But would this something for https://github.com/Icinga/icingaweb2-module-training, so it's documented for ohter interesting people?

@nilmerg
Copy link
Member

nilmerg commented Oct 15, 2024

Is there a diffrence between:

No. ::class is just another way of accessing the class path. But this way, it mandates a use … at the top, which lets PHP verify its existence.

And by looking at your class path, MyExternalBackend cannot be the right module name. Please try Myexternalbackend instead.

so it's documented for ohter interesting people?

You already think that this is somewhat quirky to extend ExternalBackend and I agree. So no, I don't want to document this :D

@stevie-sy
Copy link
Author

No. ::class is just another way of accessing the class path. But this way, it mandates a use … at the top, which lets PHP verify its existence.

Thanks for clarification. Both ways works for me.

And by looking at your class path, MyExternalBackend cannot be the right module name. Please try Myexternalbackend instead.

It was just a copy&paste mistake from your example. My class name in my module is correct - don't worry ☺️

You already think that this is somewhat quirky to extend ExternalBackend and I agree. So no, I don't want to document this

I didn't meant to document to extend ExternalBackend for own backends, I meant the suggestions for $this->provideUserBackend for the docs 😉

Now everyone accessing my Web is an admin 🎉

My module is also working. That is not the problem. But did you check your icingaweb2.log as well in your test? Because as I wrote initially, there are are lot of error messages, until the module with the own backend is loaded. And that is what I mean the own module with the own backend is loaded too late 😉

@nilmerg
Copy link
Member

nilmerg commented Oct 16, 2024

My module is also working. That is not the problem. But did you check your icingaweb2.log as well in your test?

Depending on the situation icingaweb produces a lot of error messages like:

🤦‍♂️

Clearly didn't read this. 🙄

Yes I have these messages. And I know why. It's because (in my case) businessprocess initiates authentication on its own during init. I knew, and I think I've told you this as well in 2022, that doing this is discouraged. I just didn't knew why until now.

Now we're doing this ourselves 😭

I meant the suggestions for $this->provideUserBackend for the docs 😉

You can always make a suggestion for https://github.com/Icinga/icingaweb2-module-training :D

@nilmerg nilmerg added bug Something isn't working area/framework Affects third party integration/development labels Oct 16, 2024
@stevie-sy
Copy link
Author

stevie-sy commented Oct 16, 2024

Yes I think you told me this 😉

Now we're doing this ourselves

I found this e.g. in run.php or configuration.php in following modules we are using:

  • icingaweb-db,
  • the old monitoring module,
  • grafana (ok, this is not yours)
  • as well in some hooks of the modules (e.g. IcingadbSupportHook.php)

And if I'm correct for every module which is calling Auth::getInstance() there is an error message in icingaweb2.log until the own backend is loaded.

You can always make a suggestion for https://github.com/Icinga/icingaweb2-module-training :D

Yes I know, but there is still the unresolved question with your CLA from our bosses 😥

@nilmerg
Copy link
Member

nilmerg commented Oct 17, 2024

I meant a suggestion in form of an issue, suggesting to add this to the training or introduce one. Not actually introducing this by yourself. ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/framework Affects third party integration/development bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants