Michael Krotscheck b3239ba248 OAuth check for response_type 'code'
This patch adds sanity checking for response_type 'code'. It does
this by switching errors within the OAuth controller to use
exception handling rather than explicit response creation, so that we
can more easily raise errors encountered during our validation process.

Change-Id: I4f3821f8f63f4102def95e407c59c81571c0bb55
2015-02-06 09:24:17 -08:00

137 lines
4.3 KiB
Python

# Copyright (c) 2014 Mirantis Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
from oslo_log import log
import pecan
from pecan import request
from pecan import response
from pecan import rest
import six
from storyboard.api.auth.oauth_validator import SERVER
from storyboard.api.auth.openid_client import client as openid_client
from storyboard.common import decorators
from storyboard.db.api import auth as auth_api
LOG = log.getLogger(__name__)
class AuthController(rest.RestController):
_custom_actions = {
"authorize": ["GET"],
"authorize_return": ["GET"],
"token": ["POST"],
}
@decorators.oauth_exceptions
@pecan.expose()
def authorize(self):
"""Authorization code request."""
return openid_client.send_openid_redirect(request, response)
@decorators.oauth_exceptions
@pecan.expose()
def authorize_return(self):
"""Authorization code redirect endpoint.
At this point the server verifies an OpenId and retrieves user's
e-mail and full name from request
The client may already use both the e-mail and the fullname in the
templates, even though there was no token request so far.
"""
if not openid_client.verify_openid(request, response):
# The verify call will set unauthorized code
return response
headers, body, code = SERVER.create_authorization_response(
uri=request.url,
http_method=request.method,
body=request.body,
scopes=request.params.get("scope"),
headers=request.headers)
response.headers = dict((str(k), str(v))
for k, v in six.iteritems(headers))
response.status_code = code
response.body = body or ''
return response
def _access_token_by_code(self):
auth_code = request.params.get("code")
code_info = auth_api.authorization_code_get(auth_code)
headers, body, code = SERVER.create_token_response(
uri=request.url,
http_method=request.method,
body=request.body,
headers=request.headers)
response.headers = dict((str(k), str(v))
for k, v in six.iteritems(headers))
response.status_code = code
json_body = json.loads(body)
# Update a body with user_id only if a response is 2xx
if code / 100 == 2:
json_body.update({
'id_token': code_info.user_id
})
response.body = json.dumps(json_body)
return response
def _access_token_by_refresh_token(self):
refresh_token = request.params.get("refresh_token")
refresh_token_info = auth_api.refresh_token_get(refresh_token)
headers, body, code = SERVER.create_token_response(
uri=request.url,
http_method=request.method,
body=request.body,
headers=request.headers)
response.headers = dict((str(k), str(v))
for k, v in six.iteritems(headers))
response.status_code = code
json_body = json.loads(body)
# Update a body with user_id only if a response is 2xx
if code / 100 == 2:
json_body.update({
'id_token': refresh_token_info.user_id
})
response.body = json.dumps(json_body)
return response
@decorators.oauth_exceptions
@pecan.expose()
def token(self):
"""Token endpoint."""
grant_type = request.params.get("grant_type")
if grant_type == "authorization_code":
# Serve an access token having an authorization code
return self._access_token_by_code()
if grant_type == "refresh_token":
# Serve an access token having a refresh token
return self._access_token_by_refresh_token()