diff --git a/scripts/federated-login.sh b/scripts/federated-login.sh new file mode 100755 index 0000000000..c6a199df61 --- /dev/null +++ b/scripts/federated-login.sh @@ -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 " "--domain " "" >&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 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.json +echo - Obtained scoped token from SP for project $PROJECT in domain $DOMAIN: $SCOPED_TOKEN +echo - Full catalog available in file catalog.json + +cat <_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=') +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