Wrapper script to perform K2K federated login

This patch adds a small script that automates the process of accessing a
service provider (SP) cloud using credentials from a identity provider
cloud (IdP), where both clouds use Keystone based authentication. The
script performs the complete authentication flow and displays the token
and endpoints to use with the openstack command line client.

Implements: blueprint keystone-federation
Change-Id: I4b8113d0aef9c754fb55497d44138df660332bb8
This commit is contained in:
Miguel Grinberg 2015-07-20 16:14:11 -07:00 committed by Jesse Pretorius
parent 126b2e7d8e
commit 27831a026f

160
scripts/federated-login.sh Executable file
View File

@ -0,0 +1,160 @@
#!/bin/bash
# Copyright 2015, Rackspace US, 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.
# defaults
DOMAIN=Default
PROJECT=
SP_ID=
usage()
{
echo Usage: $0 "--project <project-name>" "--domain <domain-name>" "<service-provider-name>" >&2
echo "Options:" >&2
echo " -p | --project The project on the SP cloud to log in to." >&2
echo " -d | --domain The domain on the SP cloud to log in to. The default domain is used if not specified." >&2
exit 1
}
while [[ $# > 0 ]]
do
key="$1"
case $key in
-d|--domain)
DOMAIN="$2"
shift
;;
-p|--project)
PROJECT="$2"
shift
;;
-h|--help)
usage
;;
*)
break
;;
esac
shift
done
SP_ID=$1
if [ "$DOMAIN" == "" ]; then
echo Error: Domain must be specified.
fi
if [ "$PROJECT" == "" ]; then
echo Error: Project must be specified.
fi
if [ "$SP_ID" == "" ]; then
echo Error: Service provider must be specified.
fi
if [ "$DOMAIN" == "" -o "$PROJECT" == "" -o "$SP_ID" == "" ]; then
usage
fi
echo Performing federated login...
# obtain a scoped token from the identity provider
curl -v -s -X POST -H "Content-Type: application/json" -d '{"auth":{"identity":{"methods":["password"],"password":{"user":{"name":"'"$OS_USERNAME"'","password":"'"$OS_PASSWORD"'","domain":{"name":"'"$OS_USER_DOMAIN_NAME"'"}}}}}}' $OS_AUTH_URL/auth/tokens >token.json 2>token.txt
if [ "$?" != "0" ]; then
echo "Could not obtain IdP token, did you forget to import your openrc file? See token.json and error.log for details."
exit 1
fi
IDP_TOKEN=`grep X-Subject-Token token.txt | grep -Po ': .*' | grep -Po '[a-zA-Z0-9-_]*'`
echo - Obtained IdP token.
# obtain the service provider URLs
python -c "import json; t = json.loads(open('token.json').read()); sp = [x for x in t['token']['service_providers'] if x['id'] == '$SP_ID']; print('SP_URL='+sp[0][\"sp_url\"]+'\nSP_AUTH_URL='+sp[0][\"auth_url\"] if len(sp) > 0 else '')" > sp.txt
source sp.txt
if [ "$SP_URL" == "" -o "$SP_AUTH_URL" == "" ]; then
echo "Could not find service provider $SP_ID."
exit 1
fi
SP_KEYSTONE_V3_URL=`echo $SP_AUTH_URL | grep -Po "(.*/v3)"`
# request a SAML2 assertion from the identity provider
curl -s -X POST -H "X-Auth-Token: $IDP_TOKEN" -H "Content-Type: application/json" -d '{"auth": {"scope": {"service_provider": {"id": "'"$SP_ID"'"}}, "identity": {"token": {"id":"'"$IDP_TOKEN"'"}, "methods": ["token"]}}}' $OS_AUTH_URL/auth/OS-FEDERATION/saml2/ecp >assertion.xml 2>error.log
if [ "$?" != "0" ] || grep -q error assertion.xml; then
echo Could not obtain SAML2 assertion. See assertion.xml and error.log for details.
exit 1
fi
echo - Obtained SAML2 assertion from IdP.
# send the assertion to the service provider
curl -s -X POST -H "Content-Type: application/vnd.paos+xml" -c cookies.txt -d "@assertion.xml" $SP_URL >error.log 2>&1
if [ "$?" != "0" ]; then
echo The assertion was not accepted by the service provider. See error.log for details.
exit 1
fi
echo - Submitted SAML2 assertion to SP.
# request an unscoped token from the service provider
curl -v -s -X GET -H "Content-Type: application/vnd.paos+xml" -b cookies.txt $SP_AUTH_URL >/dev/null 2>unscoped.txt
if [ "$?" != "0" ] || ! grep -q X-Subject-Token unscoped.txt; then
echo Could not obtain unscoped token from service provider. See unscoped.txt and error.log for details.
exit 1
fi
UNSCOPED_TOKEN=`grep X-Subject-Token unscoped.txt | grep -Po ': .*' | grep -Po '[a-zA-Z0-9-_]*'`
echo - Obtained unscoped token from SP: $UNSCOPED_TOKEN
echo '- Domains available at sp: '
curl -v -s -X GET -H "X-Auth-Token: $UNSCOPED_TOKEN" "${SP_KEYSTONE_V3_URL}/OS-FEDERATION/domains" 2>error.log | python -m json.tool |awk '/"name":/{print $2}'
echo '- Projects available at sp: '
curl -v -s -X GET -H "X-Auth-Token: $UNSCOPED_TOKEN" \
"${SP_KEYSTONE_V3_URL}/OS-FEDERATION/projects" \
>fed_projects.json \
2>error.log
python -m json.tool <fed_projects.json |awk '/"name":/{print $2}'
grep -q $PROJECT fed_projects.json || { echo "$PROJECT is not available at $SP_ID"; exit 1; }
# exchange the unscoped token for a scoped token
curl -v -s -X POST -H "X-Auth-Token: $UNSCOPED_TOKEN" -H "Content-Type: application/json" -d '{"auth":{"identity":{"methods":["saml2"],"saml2":{"id":"'"$UNSCOPED_TOKEN"'"}},"scope":{"project":{"domain": {"name": "'"$DOMAIN"'"},"name":"'"$PROJECT"'"}}}}' $SP_KEYSTONE_V3_URL/auth/tokens >catalog.txt 2>scoped.txt
if [ "$?" != "0" ] || grep -q 401 scoped.txt; then
echo Could not obtain scoped token and catalog from service provider. See scoped.txt and catalog.txt for details.
exit 1
fi
SCOPED_TOKEN=`awk '/X-Subject-Token/{print $3}' scoped.txt`
python -m json.tool <catalog.txt >catalog.json
echo - Obtained scoped token from SP for project $PROJECT in domain $DOMAIN: $SCOPED_TOKEN
echo - Full catalog available in file catalog.json
cat <<EOF >_print_vars.py
import json
import sys
token = sys.argv[1]
catalog = json.loads(open(sys.argv[2]).read())
print('#----------------------------------------')
print('# Available endpoints:')
for service in catalog['token']['catalog']:
svc_type = service['type']
for endpoint in service['endpoints']:
if endpoint['interface'] == 'public':
svc_endpoint = endpoint['url']
print(svc_type.upper().replace('-', '_') + '_URL=' + svc_endpoint)
print('#----------------------------------------')
print('# OpenStack client setup:')
print('export OS_TOKEN=' + token)
print('export OS_URL=<desired-service-endpoint>')
EOF
python _print_vars.py $SCOPED_TOKEN catalog.txt
# cleanup
rm token.json token.txt sp.txt assertion.xml cookies.txt unscoped.txt scoped.txt catalog.txt error.log _print_vars.py fed_projects.json