oidc: deprecate grant_type argument

The OpenID Connect grant type is something implicit to our plugins,
so there is no need to pass it as a keyword argument, we can define
this as a class argument. Moreover, it makes no sense to pass a
grant_type that is different to the grant type that is being
implemented. This change deprecates the keyword argument (showing a
warning to the users if they're passing it) and checking if the
grant_type is different to the one defined by the plugin itself.

Change-Id: I4fffc2c651487d3240bbc8458ad728b06b0ffa64
This commit is contained in:
Alvaro Lopez Garcia 2016-06-16 11:40:29 +02:00
parent 00746ea636
commit 885aff00ac
3 changed files with 64 additions and 18 deletions

View File

@ -16,7 +16,7 @@ from keystoneauth1.exceptions import auth_plugins
__all__ = ( __all__ = (
'InvalidDiscoveryEndpoint', 'InvalidOidcDiscoveryDocument', 'InvalidDiscoveryEndpoint', 'InvalidOidcDiscoveryDocument',
'OidcAccessTokenEndpointNotFound', 'OidcAuthorizationEndpointNotFound', 'OidcAccessTokenEndpointNotFound', 'OidcAuthorizationEndpointNotFound',
'OidcPluginNotSupported', 'OidcGrantTypeMissmatch', 'OidcPluginNotSupported',
) )
@ -36,5 +36,9 @@ class OidcAuthorizationEndpointNotFound(auth_plugins.AuthPluginException):
message = "OpenID Connect authorization endpoint not provided." message = "OpenID Connect authorization endpoint not provided."
class OidcGrantTypeMissmatch(auth_plugins.AuthPluginException):
message = "Missmatch between OpenID Connect plugin and grant_type argument"
class OidcPluginNotSupported(auth_plugins.AuthPluginException): class OidcPluginNotSupported(auth_plugins.AuthPluginException):
message = "OpenID Connect grant type not supported by provider." message = "OpenID Connect grant type not supported by provider."

View File

@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import warnings
from positional import positional from positional import positional
from keystoneauth1 import _utils as utils from keystoneauth1 import _utils as utils
@ -31,12 +33,16 @@ class _OidcBase(federation.FederationBaseAuth):
``http://openid.net/specs/openid-connect-core-1_0.html`` ``http://openid.net/specs/openid-connect-core-1_0.html``
""" """
grant_type = None
def __init__(self, auth_url, identity_provider, protocol, def __init__(self, auth_url, identity_provider, protocol,
client_id, client_secret, grant_type, client_id, client_secret,
access_token_type, access_token_type,
scope="openid profile", scope="openid profile",
access_token_endpoint=None, access_token_endpoint=None,
discovery_endpoint=None, **kwargs): discovery_endpoint=None,
grant_type=None,
**kwargs):
"""The OpenID Connect plugin expects the following. """The OpenID Connect plugin expects the following.
:param auth_url: URL of the Identity Service :param auth_url: URL of the Identity Service
@ -55,14 +61,6 @@ class _OidcBase(federation.FederationBaseAuth):
:param client_secret: OAuth 2.0 Client Secret :param client_secret: OAuth 2.0 Client Secret
:type client_secret: string :type client_secret: string
:param grant_type: OpenID Connect grant type, it represents the flow
that is used to talk to the OP. Valid values are:
"authorization_code", "refresh_token", or
"password". If a discovery document is being used,
this class will check if the provided value is
supported by the provider.
:type grant_type: string
:param access_token_type: OAuth 2.0 Authorization Server Introspection :param access_token_type: OAuth 2.0 Authorization Server Introspection
token type, it is used to decide which type token type, it is used to decide which type
of token will be used when processing token of token will be used when processing token
@ -101,7 +99,18 @@ class _OidcBase(federation.FederationBaseAuth):
self.access_token_type = access_token_type self.access_token_type = access_token_type
self.scope = scope self.scope = scope
self.grant_type = grant_type if grant_type is not None:
if grant_type != self.grant_type:
raise exceptions.OidcGrantTypeMissmatch()
warnings.warn("Passing grant_type as an argument has been "
"deprecated as it is now defined in the plugin "
"itself. You should stop passing this argument "
"to the plugin, as it will be ignored, since you "
"cannot pass a free text string as a grant_type. "
"This argument will be dropped from the plugin in "
"July 2017 or with the next major release of "
"keystoneauth (3.0.0)",
DeprecationWarning)
def _get_discovery_document(self, session): def _get_discovery_document(self, session):
"""Get the contents of the OpenID Connect Discovery Document. """Get the contents of the OpenID Connect Discovery Document.
@ -231,13 +240,16 @@ class _OidcBase(federation.FederationBaseAuth):
class OidcPassword(_OidcBase): class OidcPassword(_OidcBase):
"""Implementation for OpenID Connect Resource Owner Password Credential.""" """Implementation for OpenID Connect Resource Owner Password Credential."""
grant_type = "password"
@positional(4) @positional(4)
def __init__(self, auth_url, identity_provider, protocol, def __init__(self, auth_url, identity_provider, protocol,
client_id, client_secret, client_id, client_secret,
access_token_endpoint=None, access_token_endpoint=None,
discovery_endpoint=None, discovery_endpoint=None,
grant_type='password', access_token_type='access_token', access_token_type='access_token',
username=None, password=None, **kwargs): username=None, password=None,
**kwargs):
"""The OpenID Password plugin expects the following. """The OpenID Password plugin expects the following.
:param username: Username used to authenticate :param username: Username used to authenticate
@ -254,7 +266,6 @@ class OidcPassword(_OidcBase):
client_secret=client_secret, client_secret=client_secret,
access_token_endpoint=access_token_endpoint, access_token_endpoint=access_token_endpoint,
discovery_endpoint=discovery_endpoint, discovery_endpoint=discovery_endpoint,
grant_type=grant_type,
access_token_type=access_token_type, access_token_type=access_token_type,
**kwargs) **kwargs)
self.username = username self.username = username
@ -297,12 +308,13 @@ class OidcPassword(_OidcBase):
class OidcAuthorizationCode(_OidcBase): class OidcAuthorizationCode(_OidcBase):
"""Implementation for OpenID Connect Authorization Code.""" """Implementation for OpenID Connect Authorization Code."""
grant_type = 'authorization_code'
@positional(4) @positional(4)
def __init__(self, auth_url, identity_provider, protocol, def __init__(self, auth_url, identity_provider, protocol,
client_id, client_secret, client_id, client_secret,
access_token_endpoint=None, access_token_endpoint=None,
discovery_endpoint=None, discovery_endpoint=None,
grant_type='authorization_code',
access_token_type='access_token', access_token_type='access_token',
redirect_uri=None, code=None, **kwargs): redirect_uri=None, code=None, **kwargs):
"""The OpenID Authorization Code plugin expects the following. """The OpenID Authorization Code plugin expects the following.
@ -322,7 +334,6 @@ class OidcAuthorizationCode(_OidcBase):
client_secret=client_secret, client_secret=client_secret,
access_token_endpoint=access_token_endpoint, access_token_endpoint=access_token_endpoint,
discovery_endpoint=discovery_endpoint, discovery_endpoint=discovery_endpoint,
grant_type=grant_type,
access_token_type=access_token_type, access_token_type=access_token_type,
**kwargs) **kwargs)
self.redirect_uri = redirect_uri self.redirect_uri = redirect_uri
@ -390,7 +401,6 @@ class OidcAccessToken(_OidcBase):
client_id=None, client_id=None,
client_secret=None, client_secret=None,
access_token_endpoint=None, access_token_endpoint=None,
grant_type=None,
access_token_type=None, access_token_type=None,
**kwargs) **kwargs)
self.access_token = access_token self.access_token = access_token

View File

@ -11,6 +11,7 @@
# under the License. # under the License.
import uuid import uuid
import warnings
from six.moves import urllib from six.moves import urllib
@ -48,6 +49,33 @@ class BaseOIDCTests(object):
self.DISCOVERY_URL = ('https://localhost:8020/oidc/.well-known/' self.DISCOVERY_URL = ('https://localhost:8020/oidc/.well-known/'
'openid-configuration') 'openid-configuration')
self.GRANT_TYPE = None
def test_grant_type_and_plugin_missmatch(self):
self.assertRaises(
exceptions.OidcGrantTypeMissmatch,
self.plugin.__class__,
self.AUTH_URL,
self.IDENTITY_PROVIDER,
self.PROTOCOL,
client_id=self.CLIENT_ID,
client_secret=self.CLIENT_SECRET,
grant_type=uuid.uuid4().hex
)
def test_can_pass_grant_type_but_warning_is_issued(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
self.plugin.__class__(
self.AUTH_URL,
self.IDENTITY_PROVIDER,
self.PROTOCOL,
client_id=self.CLIENT_ID,
client_secret=self.CLIENT_SECRET,
grant_type=self.GRANT_TYPE)
assert len(w) == 1
assert issubclass(w[-1].category, DeprecationWarning)
assert "grant_type" in str(w[-1].message)
def test_discovery_not_found(self): def test_discovery_not_found(self):
self.requests_mock.get("http://not.found", self.requests_mock.get("http://not.found",
@ -156,6 +184,8 @@ class OIDCPasswordTests(BaseOIDCTests, utils.TestCase):
def setUp(self): def setUp(self):
super(OIDCPasswordTests, self).setUp() super(OIDCPasswordTests, self).setUp()
self.GRANT_TYPE = 'password'
self.plugin = oidc.OidcPassword( self.plugin = oidc.OidcPassword(
self.AUTH_URL, self.AUTH_URL,
self.IDENTITY_PROVIDER, self.IDENTITY_PROVIDER,
@ -228,6 +258,8 @@ class OIDCAuthorizationGrantTests(BaseOIDCTests, utils.TestCase):
def setUp(self): def setUp(self):
super(OIDCAuthorizationGrantTests, self).setUp() super(OIDCAuthorizationGrantTests, self).setUp()
self.GRANT_TYPE = 'authorization_code'
self.plugin = oidc.OidcAuthorizationCode( self.plugin = oidc.OidcAuthorizationCode(
self.AUTH_URL, self.AUTH_URL,
self.IDENTITY_PROVIDER, self.IDENTITY_PROVIDER,