Keystone auth component to add existing user database

Asked by Phil

Hey there,

I'm trying to get a hold on the best approach to make keystone aware of an existing (internal) user database, that's
exposed via an API.

The flow should have been:

- extract username and password
- encrypt them
- check the API of the internal DB if the credentials are valid
- If so, return None
- if not, raise Unauthorized

There are basically two way's

- write a plugin (http://docs.openstack.org/developer/keystone/configuration.html#authentication-plugins)
- write a middleware component (http://docs.openstack.org/developer/keystone/external-auth.html)

So first I tried to write a plugin, as this seemed to be the right approach, so the basic implementation had the authenticate method,
that was just logging the auth attempt and raise Unauthorized. I could see, through other logging extensions, that the auth plugin got instantiated etc. but never used.

So tracing what would happen, I came to the conclusion, that I need a middleware component, as authentication was always done
through

keystone.token.controllers.Auth.authenticate.

which does not have the fancy checking and list building as

keystone.auth.controllers.Auth to check the config file for the list 'methods' and try each method in turn.
I assumed this is due to the fact, that somehow, keystone is always used in an http fashion.

So I went back to the middleware component. I'll start work on this more or less after I've finished this post.
But I'm a little irritated as to what the approach here would be. There are also a couple of open questions, like, [Middleware] Is the token generated after I set REMOTE_USER or do I have to trigger this manually?

The flow would be similar to the plugin flow. Though what I still lack is a comprehensive overview, of how authentication is
done in context of a call http://openstack.wherever.org:5000/v2.0/tokens.

- Which contexts [i.e. API call's, rpc call's, http contexts, ] are there, that use the concept of authentication?
- How is authentication done for these contexts?
- Under which conditions would a plugin be used?
- Under which conditions would a middleware be used?
- Why does it seem that the plugin chain is not used in the flow I described?
- Why do I get the feeling that I'm completely misunderstanding the context, in which plugin and middleware are used?

I hope anyone could point me in the right direction of getting a more general understanding of the auth architecture, beyond
http://docs.openstack.org/developer/keystone/, which practically gave me not the information that I'm looking for.

Best Regards,
Phil

Question information

Language:
English Edit question
Status:
Answered
For:
OpenStack Identity (keystone) Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:
Revision history for this message
Haneef Ali (haneef) said :
#1

You need to decide first whether you are going to use V2 or V3 api. It looks like you are using v2.0 from your question. The plugins are used only in v3.

Use v3, and overrdie the password plugin to do your won authentication

Revision history for this message
Phil (p.g.sonntag) said :
#2

Thank you very much, I was afraid of something like that ;-)

I am, I guess, using v2 of the API. Though we want to be using v3.
I'll look into this as soon as I can and post the results here. Should, ATM, only be a
change of URL in the call.
Or do I explicitly need to configure an identity endpoint in keystone with 'v3' in the endpoint URL.

Revision history for this message
Phil (p.g.sonntag) said :
#3

Ok, one part was v3 API, that's working now. Using a small client to interact with v3 returns tokens etc.

The other thing is the plugin, especially the configuration to make keystone aware of it.

On http://docs.openstack.org/developer/keystone/configuration.html#how-to-implement-an-authentication-plugin
it's stated, that you need to add your own plugin in the list 'methods' in the [auth] section of keystone.conf and add the proper configuration where the plugin is stored. That works, as I can see that it get's instantiated. And the docs also said, that plugins are
executed in the order that they are listed in the list.

But the docstring in the source code for keystone.auth.core.py says, that there should be a similar parameter in the
[identity] section. I'm a little reluctant in adding , but it say's:

 Plugins are invoked in the order in which they are specified in the
        ``methods`` attribute of the ``identity`` object.

From what I can see in my test case, the only method that get's called through keystone.auth.controllers.Auth.authenticate is
'password'. So I went ahead and added a list to the [identity] section. Result so far: Nothing changed.

The call to auth_info.get_method_names() in keystone.auth.controllers.Auth.authenticate still only return 'password'.

But as this is additional authentication alongside regular keystone authentication (and based on docs I found) I'd say that I
can configure Keystone to recognize the plugin instead of overriding 'password'.

Any ideas on the config?

Revision history for this message
Haneef Ali (haneef) said :
#4

BTW all the services are still using v2. So you need to decide whether you want to override v2 or v2.

For v3:
---------
  1) method is a new method of authentication. Since you are sitll going to use "password", you need to override password plugin.
I beleive you have written a custome method and plugin for the custom method, if that is the case , then your request should look like

{
    "auth": {
        "identity": {
            "methods": [
                "MyCustomMethod"
            ],
            "MyCustomMethod": {
                "user": {
                    "id": "0ca8f6",
                    "password": "secrete"
                }
            }
        }
I
    }
}

In this case you "MyCustomMethod" plugin will be invoked. I don't think you want to do this, since now your clients needs to undestand your "MyCustomMethod"

So your best bet is is overfide authenticate method in "password" plugin , so the request looks like typical openstack request.

For v2:
---------
  keystone/tokens/controller.py -- class Auth method "authenticate" is what you want to override

Revision history for this message
Phil (p.g.sonntag) said :
#5

There's a link, that's mysteriously "vanished", that not only provided a skeleton for a plugin, but also
outlined the invocation chain for the plugins and the alteration needed in the [auth] section. I'm sorry,
but I can't find it anymore. The outline from the source, similar to what you've posted above:

Plugins are invoked in the order in which they are specified in the
``methods`` attribute of the ``identity`` object. For example,
``custom-plugin`` is invoked before ``password``, which is invoked
        before ``token`` in the following authentication request::

            {
                "auth": {
                    "identity": {
                        "custom-plugin": {
                            "custom-data": "sdfdfsfsfsdfsf"
                        },
                        "methods": [
                            "custom-plugin",
                            "password",
                            "token"
                        ],
                        "password": {
                            "user": {
                                "id": "s23sfad1",
                                "password": "secrete"
                            }
                        },
                        "token": {
                            "id": "sdfafasdfsfasfasdfds"
                        }
                    }
                }
            }

Regarding the plugin awareness of the client, that's no problem, that's a part that we are doing too, so no problem there.
And I'd rather stick with a list of methods that is cycled through until one was successful than overwriting an existing
modul. In the end, I need both way's for authentication, password and custom.

If I extend my auth request, like so:

{"auth":
    {"identity":
        {"methods": [
             "password",
             "custom"
         ],
         "password": {
             "user": {
                 "domain": {
                     "name": "Default"
                      },
                     "name": "myName",
                     "password": "MyPassword"
       }
    }
}

And to start out, leaving out the custom-plugin data, I get:

{u'error': {u'code': 400,
            u'message': u'Expecting to find custom in identity. The server could not comply with the request since it is either malformed or otherwise incorrect. The client is assumed to be in error.',
            u'title': u'Bad Request'}}

The keystone.conf does have the proper configuration:

*snippet*

[auth]
methods = custom,external,password,token,oauth1
#external = keystone.auth.plugins.external.ExternalDefault
custom = keystone.auth.plugins.custom.MyCustomAuth

*snippet*

Whereas the plugin does have a constant "METHOD_NAME = 'custom' ".
And, adding logging to the proper file, I can see that it get's instantiated etc. as mentioned before.

So, to summarize:

- I can specify it as an additional custom method, as I also have control over the client requesting auth
- I still need to figure out, how to configure keystone, to recognize and use the plugin.

Many thanks so far, each answer is bringing me a step closer to solving this.

Revision history for this message
Haneef Ali (haneef) said :
#6

Now I understand your requirement. What you are doing is correct and it should work. So what is the error you are getting for the first request. Obvously the second is wrong since there is no body for "custom" method.

Just recently I did this for OTP, so I'm sure it works. I didn't check token along with otp, but I checked password. So first try simple custome plugin along with password and post the error .

FYI My request looked like

 {
                "auth": {
                    "identity": {
                        "otp": {
                            "otp_value": "123456"
                        },
                        "methods": [
                            "password",
                            "otp"
                        ],
                        "password": {
                            "user": {
                                "id": "s23sfad1",
                                "password": "secrete"
                            }
                        }
                    }
                }
            }

Revision history for this message
Phil (p.g.sonntag) said :
#7

Well firstly, thanks again and sorry for not making it clear to begin with.

Secondly, you're right, for as long as there's no 'custom' body in the message, just the plugin name in the methods list, it'll fail.
I did not add the body for the method, as I wanted to firstly try and see, if the method in 'methods' would show up
in the list, like it does now, with a body for 'custom':

2014-03-27 08:42:50.301 23072 ERROR keystone.auth.controllers [-] keystone.auth.controllers.Auth.authenticate method:
<keystone.auth.plugins.password.Password object at 0x3060190>
2014-03-27 08:42:50.370 23072 ERROR keystone.auth.controllers [-] keystone.auth.controllers.Auth.authenticate method:
<keystone.auth.plugins.custom.MyCustomAuth object at 0x1a5f610>

For now, it seems to be working now. Albeit not as expected. The custom plugin does, ATM nothing more than raising
Unauthorized. And 'password' is still in place. But I get:

Authorization failed. The request you have made requires authentication.

Even if the first method used is 'password'. Seems that it now needs confirmation from both plugins, if both
plugins are specified in the request? 'password' should have worked, the credentials are correct etc. If I raise
Unauthorized in the custom plugin, I can see that specifying the plugins in a different order would change the
order of execution. So that's as expected, but it seems that the second plugin is always executed and that both
need to return None in order for the authentication to succeed. Is this correct?

If I change the custom plugin to just return None, i can again see, that both plugins are executed, and now,
authentication succeeds. I see two possibilities here. In form of questions these are

First:

Is there a way to configure it so that authentication will succeed if either of the plugins return None? So I can
skip any credential awareness in the client and just let keystone check both systems, in a specified order. Like:
Case A:
if the first raises Unauthorized, then check the next, if that returns None, then it's ok. Otherwise deny access.

Case B:
if the first returns None, skip the second and authorize, if it raises Unauthorized, check the second etc.

Second:

Or do I have to make my client aware of the credentials entered and then let the client decide which plugin to
use in the request?

I would prefer it the first way, just having the client specify both methods and let keystone decide.

BTW: I'd like to push the "This solved my Problem" Button. Though I still have questions, the initial one is solved.
But I'm afraid, that adding comments would be disabled, once it's solved.

Would it make sense, to close this here and raise a second question? Or is it ok to keep this open?
I guess, once I'm clear about the two possibilities regarding specifying the methods etc., this issue
would finally be closed for me.

Thanks again.

Revision history for this message
Haneef Ali (haneef) said :
#8

Keystone requires both to succed. There is no config settings to get your usecase. You can always monkey_patch the keystone.auth.controller-> authenticate method to get the desired behavior which suits your first use case. That's what I will do.

Can you help with this problem?

Provide an answer of your own, or ask Phil for more information if necessary.

To post a message you must log in.