SAML is usually thought of as a WebSSO mechanism, but it can be made to work for command line operations if you use the Extended Client Protocol (ECP). When we did the Rippowam demo last year, we were successful in getting an Unscoped token by using ECP, but that was not sufficient to perform operations on other services that need a scoped token.
The general approach that we are looking at for Keystone is to always have the user ask for an unscoped token first, and then upgrade that to a scoped token. The scoping process can only be done from unscoped to scoped (configuration option) to prevent elevation of privilege attacks.
The base federation plugin is capable of handling this kind of workflow. Thus, the general approach is to write a protocol specific plugin to get an unscoped token, and then to use common logic in the base class v3.FederatedBaseAuth to convert unscoped to scoped.
I just got [edit: used to say keystone] opentstack flavor list to work with ECP and Keycloak. I had to create a new auth plugin to do it:
Created a new entry point in
v3fedsaml = keystoneclient.contrib.auth.v3.saml2:FederatedSAML2
Added this to
class FederatedSAML2(v3.FederatedBaseAuth): """Authenticate using SAML via the keystone federation mechanisms. Wraps both the unscoped SAML2 Plugin to 1. Request an unscoped token 2. Use the unscoped token to request a scoped token """ @classmethod def get_options(cls): options = super(FederatedSAML2, cls).get_options() options.extend([ cfg.StrOpt('identity-provider', help="Identity Provider's name"), cfg.StrOpt('protocol', help="SAML2"), cfg.StrOpt('identity-provider-url', help="Identity Provider's URL"), cfg.StrOpt('user-name', dest='username', help='Username', deprecated_name='username'), cfg.StrOpt('password', help='Password') ]) return options def __init__(self, auth_url, identity_provider, protocol, identity_provider_url, username, password, **kwargs): #protocol = kwargs.pop('protocol') super(FederatedSAML2, self).__init__(auth_url, identity_provider, protocol, **kwargs) self._unscoped = Saml2UnscopedToken(auth_url, identity_provider, identity_provider_url, username, password, **kwargs) def get_unscoped_auth_ref(self, session, **kwargs): return self._unscoped.get_auth_ref(session, **kwargs)
Updated my keystone RC file:
This is based on RH OSP8 which is Liberty release. In later releases of OSP, the client libraries are synchronized with later versions, including the gradual replacement of keystoneauth for the Auth plugins housed in python-keystone. Thus, there will be a couple variations on this plauoing, including one that may have to live out of tree if we want it for OSP8.