IdP iniciado
Esta seção usa o script Client4ShibbolethIdP como um exemplo para descrever como obter um token de autenticação federado no modo iniciado por IdP. O script Client4ShibbolethIdP simula um usuário fazendo logon em um sistema de IdP usando um navegador, ajudando você a desenvolver seu próprio script de cliente IdP.
Pré-requisitos
- Seu servidor de IdP oferece suporte à autenticação de identidade federada iniciada pelo IdP.
- A biblioteca de Python BeautifulSoup 4 foi instalada no cliente.
Fluxograma
A figura a seguir mostra o processo de autenticação de federação iniciada pelo IdP.
Descrição
- O cliente visita o link de logon fornecido pelo IdP para logon iniciado pelo IdP e define o endereço de nuvem pública (entityID no arquivo de metadados do sistema de nuvem) no link de logon.
- O cliente exibe a página de logon do IdP, permitindo que os usuários enviem informações de identidade ao IdP para autenticação.
- Depois de autenticar o usuário, o IdP constrói uma asserção contendo as informações de identidade do usuário e envia uma resposta SAML ao cliente.
- O cliente encapsula a resposta SAML e a encaminha para o sistema de nuvem para chamar a API usada para obter um token federado no modo iniciado pelo IdP.
- O sistema de nuvem verifica e autentica a asserção e gera uma credencial de acesso temporária de acordo com as regras de conversão de identidade do usuário configuradas para o provedor de identidade.
- O usuário pode acessar recursos de nuvem pública com base nas permissões atribuídas.
Implementação no cliente
Esta seção usa o script Client4ShibbolethIdP.py para descrever como implementar a autenticação de identidade federada para acesso de API/CLI ao sistema de nuvem a partir de um IdP.
Você pode baixar o script aqui:
https://obs-iam-download01.obs.cn-north-1.myhwclouds.com/non-ecp-script/Client4ShibblethIdP.py
- Configure o URL de logon do IdP.
Tabela 1 URLs de logon de produtos de IdP comuns IdP
Parâmetro de SP em URL
Exemplo de URL de logon
ADFS
logintorp
https://adfs-server.contoso.com/adfs/ls/IdpInitiatedSignon.aspx?logintorp=https://iam.example.com
Shibboleth
providerId
https://idp.example.org/idp/profile/SAML2/Unsolicited/SSO?providerId=iam.example.com
SimpleSAMLphp
spentityid
https://idp.example.org/simplesaml/saml2/idp/SSOService.php?spentityid=iam.example.com
Após a configuração, insira um URL de logon na barra de endereços de um navegador. A seguinte página é exibida.
Figura 2 Página de logon
Script Client4ShibbolethIdP:import sys import requests import getpass import re from bs4 import BeautifulSoup from urlparse import urlparse # SSL certificate verification: Whether or not strict certificate # verification is done, False should only be used for dev/test sslverification = True # Get the federated credentials from the user print "Username:", username = raw_input() password = getpass.getpass() print '' session = requests.Session() # The initial url that starts the authentication process. idp_entry_url = 'https://idp.example.com/idp/profile/SAML2/Unsolicited/SSO?providerId=https://iam.example.com' # Programmatically get the SAML assertion,open the initial IdP url# and follows all of the HTTP302 redirects, and gets the resulting# login page formresponse = session.get(idp_entry_url, verify=sslverification) # Capture the idp_authform_submit_url,which is the final url after# all the 302s idp_authform_submit_url = formresponse.url
- O cliente usa o Beautifulsoup 4 para capturar as informações do usuário e a ação solicitada e, em seguida, constrói e envia uma solicitação de autenticação de identidade para o IdP.
O cliente adquire todos os dados do formulário enviados por meio da página de logon.
Figura 3 Informações de autenticação (1)
Script Client4ShibbolethIdP:
# Parse the response and extract all the necessary values in order to build a dictionary of all of the form values the IdP expects formsoup = BeautifulSoup(formresponse.text.decode('utf8'), "lxml") payload = {} for inputtag in formsoup.find_all(re.compile('(INPUT|input)')): name = inputtag.get('name', '') value = inputtag.get('value', '') if "username" in name.lower(): payload[name] = username elif "password" in name.lower(): payload[name] = password else: payload[name] = value for inputtag in formsoup.find_all(re.compile('(FORM|form)')): action = inputtag.get('action') if action: parsedurl = urlparse(idp_entry_url) idp_authform_submit_url = parsedurl.scheme + "://" + parsedurl.netloc + action # please test on browser first, add other parameters in payload payload["_eventId_proceed"] = "" formresponse = session.post( idp_authform_submit_url, data=payload, verify=sslverification)
- O cliente analisa a próxima página. (Alguns IdPs exibem uma página de atributos de usuário.)
O cliente adquire todos os dados do formulário enviados por meio da página de logon.
Figura 4 Informações de autenticação (2)
Script Client4ShibbolethIdP:
# In shebbleth IdP v3, browser will show attributes page for user,# so we need parse the page formsoup = BeautifulSoup(formresponse.text.decode('utf8'), "lxml") payload = {} # Add other form data required from browser to payload _shib_idp_consentIds = [] for inputtag in formsoup.find_all(re.compile('input')): name = inputtag.get("name") value = inputtag.get("value") if name == "_shib_idp_consentIds": _shib_idp_consentIds.append(value) payload["_shib_idp_consentIds"] = _shib_idp_consentIds payload["_shib_idp_consentOptions"] = "_shib_idp_rememberConsent" payload["_eventId_proceed"] = "Accept" # user can get the action url from the html file nexturl = "https://idp.example.com/idp/profile/SAML2/Unsolicited/SSO?execution=e1s2" for inputtag in formsoup.find_all(re.compile('(FORM|form)')): action = inputtag.get('action') if action: parsedurl = urlparse(idp_entry_url) nexturl = parsedurl.scheme + "://" + parsedurl.netloc + action response = session.post( nexturl, data=payload, verify=sslverification)
- Se a autenticação for bem-sucedida, o cliente analisará a resposta SAML enviada pelo IdP.
Script Client4ShibbolethIdP:
# Decode the response and extract the SAML assertion soup = BeautifulSoup(response.text.decode('utf8'), "lxml") SAMLResponse = '' # Look for the SAMLResponse attribute of the input tag for inputtag in soup.find_all('input'): if (inputtag.get('name') == 'SAMLResponse'): SAMLResponse = inputtag.get('value') # Better error handling is required for production use. if (SAMLResponse == ''): print 'Response did not contain a valid SAML assertion, please troubleshooting in Idp side.' sys.exit(0)
- Obtenha um token sem escopo. Para mais detalhes, consulte Obtenção de um token sem escopo (iniciado pelo IdP).
Script Client4ShibbolethIdP:
# Set headers headers = {} headers["X-Idp-Id"] = "test_local_idp" # IAM API url: get unscoped token on IDP initiated mode sp_unscoped_token_url = "https://iam.example.com/v3.0/OS-FEDERATION/tokens" # Set form data payload = {} payload["SAMLResponse"] = SAMLResponse response = session.post( sp_unscoped_token_url, data=payload, headers=headers, verify=sslverification) # Debug only print(response.text) print "Status Code: " + str(response.status_code) if response.status_code != 201: sys.exit(1) unscoped_token = response.headers.get("X-Subject-Token") if "X-Subject-Token" in response.headers.keys() else None if unscoped_token: print ">>>>>>X-Subject-Token: " + unscoped_token
- Obtenha um token com escopo. Para mais detalhes, consulte Obtenção de um token com escopo.
Script Client4ShibbolethIdP:
payload = { "auth": { "identity": { "methods": ["token"], "token": { "id": unscoped_token } }, "scope": { "project": { "name": "{region_id}_test1" } } } } sp_scoped_token_url = "https://10.120.171.90:31943/v3/auth/tokens" response = session.post( sp_scoped_token_url, json=payload, verify=sslverification) # Debug only print "Status Code: " + str(response.status_code) if response.status_code != 201: print response.text sys.exit(1) scoped_token = response.text if response.status_code == 201 else None if scoped_token: print ">>>>>>Scoped Token:" + scoped_token
- Obtenha uma chave de acesso temporária. Para mais detalhes, consulte Obtenção de uma chave de acesso temporária e de um token de segurança por meio de um token.
Script Client4ShibbolethIdP:
# Set form data payload = { "auth": { "identity": { "methods": ["token"], "token": { "duration_seconds": "900" } } } } # Set headers headers = {} headers["X-Auth-Token"] = unscoped_token sp_STS_token_url = "https://10.120.171.90:31943/v3.0/OS-CREDENTIAL/securitytokens" response = session.post( sp_STS_token_url, json=payload, headers=headers, verify=sslverification) # Debug only print "Status Code: " + str(response.status_code) if response.status_code != 201: print response.text sys.exit(1) sts_token = response.text if response.status_code == 201 else None if sts_token: print ">>>>>>STS Token:" + sts_token