JavaOne LATAM 2015 - Segurança em Recursos RESTful com OAuth2
Minicurso Testando RESTful Web Services
-
Upload
charles-kilesse -
Category
Technology
-
view
510 -
download
0
Transcript of Minicurso Testando RESTful Web Services
Charles Kilesse@chkile
Testando RESTful Web Services
• REST e RESTful web services
• URI
• Métodos
• Estrutura de mensagem HTTP
• Códigos de resposta
• Testes exploratórios com Postman
• Testes automatizados com Python
• Boas práticas em automação
• Autenticação
Agenda
O que é REST?
REST“Representational state transfer”
Estilo arquitetural da World Wide Web
Trata de papéis e interações
Comunicação tipicamente por HTTP
RESTCliente/Servidor
Sem estado
Interface uniforme
Tá, mas… ondeentram as APIs?
RESTful Web ServicesUniform Resource Identifier (URI)
Métodos (ou verbos) HTTP
Media type (JSON, XML…)
Sem estado
URIhttp://chaordic.com.br/
http://chaordic.com.br:80/
http://user:[email protected]/
http://chaordic.com.br/caminho/recurso
URIhttp://chaordic.com.br/?chave=valor
http://chaordic.com.br/?c=v&c2=v2
http://chaordic.com.br/#fragmento
Verbos (métodos) HTTP
GET POST PUT DELETE
OPTIONS
HEADCONNECT TRACE
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
Verbos (métodos) HTTP
GET POST PUT DELETE
OPTIONS
HEADCONNECT TRACE
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
este curso
- Solicita uma representação do recurso especificado.
- Apenas recupera dados!
verbo
GET
- Envia uma entidade a ser processada.
- Dados da entidade e informações adicionais devem ser enviados no corpo da requisição.
verbo
POST
- Envia uma entidade a ser armazenada sob uma determinada URI.
- Substitui a entidade, se já existir. Se não existir, cria uma na URI especificada.
verbo
PUT
- O recurso especificado se fode é deletado.
verbo
DELETE
Verbos (métodos) HTTP
GET POST PUT DELETE
seguroidempotente
idempotente
idempotente
VERBO URI
CABEÇALHOS
CORPO
Mãos à obra!
http://www.google.com/chrome/https://www.getpostman.com/
GET http://dummyimage.com/600x400
GET http://dummyimage.com/600x400/ff0000/fff
GET http://dummyimage.com/600x400/ff0000/fff&text=October
Test
GET http://dummyimage.com/640x400/%%%
WTF!?
HTTP Status CodesInformação: 1xx
Sucesso: 2xxRedirecionamento: 3xx
Erro do Cliente: 4xxErro do Servidor: 5xx
http://www.cheatography.com/kstep/cheat-sheets/http-status-codes/
GET http://jsonplaceholder.typicode.com/posts/1
POST http://jsonplaceholder.typicode.com/posts
Content-Type: application/json
{ "title": "Titulo 1", "body": "Body 1", "userID": 1}
PUT http://jsonplaceholder.typicode.com/posts/1
Content-Type: application/json
{ "id": 1, "title": "Titulo 1", "body": "Body 1", "userID": 1}
DELETE http://jsonplaceholder.typicode.com/posts/1
#partiuautomação
https://www.jetbrains.com/pycharm/http://www.python-requests.org/
https://www.python.org/
import unittestimport requests
class TestCases(unittest.TestCase): pass
if __name__ == "__main__": unittest.main()
class TestCases(unittest.TestCase): def test_get_200(self): msg="Test not implemented." self.fail(msg)
def test_get_200(self): uri = "http://apps.testinsane.com/rte/status/200" r = requests.get(uri)
msg = "Incorrect status code: %d" % r.status_code self.assertEqual(r.status_code, 200, msg)
def test_get_401(self): uri = "http://apps.testinsane.com/rte/status/401" r = requests.get(uri)
msg = "Incorrect status code: %d" % r.status_code self.assertEqual(r.status_code, 401, msg)
POST
Enviar um payload
Header “content-type”
Validar conteúdo da resposta
import json
uri = "http://apps.testinsane.com/rte/status/200"
headers = { "content-type": "application/json"}
payload = { "id": "1", "name": "OctoberTest"}
r = requests.post(uri, data=json.dumps(payload), headers=headers)
r_payload = r.json()
msg = "Incorrect status code: %d" % r.status_codeself.assertEqual(r.status_code, 200, msg)
msg = "Incorrect id: %s" % r_payload.get("id")self.assertEqual(r_payload.get("id"), payload.get("id"), msg)
msg = "Incorrect name: %s" % r_payload.get("name")self.assertEqual(r_payload.get("name"), "payload.get("name")", msg)
def test_post_200(self): uri = "http://apps.testinsane.com/rte/status/200" headers = { "content-type": "application/json" } payload = { "id": "1", "name": "OctoberTest" } r = requests.post(uri, data=json.dumps(payload), headers=headers) r_payload = r.json()
msg = "Incorrect status code: %d" % r.status_code self.assertEqual(r.status_code, 200, msg)
msg = "Incorrect id: %s" % r_payload.get("id") self.assertEqual(r_payload.get("id"), payload.get("id"), msg)
msg = "Incorrect name: %s" % r_payload.get("name") self.assertEqual(r_payload.get("name"), "payload.get("name")", msg)
E se o host mudar?E se os endpoints mudarem?
E se a resposta tiver234565639582 chaves?
MODULARIZAR!
api_helper.py
import requestsimport json
HOST = "http://apps.testinsane.com/rte%s"
def get(endpoint): uri = HOST % endpoint return requests.get(uri)
def post(endpoint, headers, payload): uri = HOST % endpoint return requests.post(uri, data=json.dumps(payload), headers=headers)
json_helper.py
import logging
def compare(expected, actual): all_match = True for key, value in expected.iteritems(): if not value==actual.get(key): all_match = False return all_match
* E se o valor de uma chave for uma lista ou objeto?
import api_helperimport json_helper
class TestCases(unittest.TestCase): def test_get_200(self): r = api_helper.get("/status/200") msg = "Incorrect status code: %d" % r.status_code self.assertEqual(r.status_code, 200, msg)
msg = "Response payload did not match the request payload."
self.assertTrue(json_helper.compare(payload, r_payload), msg)
BOAS PRÁTICAS!
Organizando os testes com o padrão “3 As”
def test_get_200(self): # Arrange endpoint = "/status/200" # Act r = api_helper.get(endpoint) # Assert msg = "Incorrect status code: %d" % r.status_code self.assertEqual(r.status_code, 200, msg)
Funcional x Estruturallogin(username, password)
ou
login_as(user)
enter_name(username)
enter_password(password)
click_login()
Use “lint checkers”!(PEP8: Style Guide for Python Code)
Código de teste entrega valor e deve ser tratado como “código de produção”
YAGNI, DRY and KISS whenever you can!
AUTENTICAÇÃO
Basic AuthenticationAutenticação simples (cabeçalhos
estáticos)
Encoding em Base64 durante a trasnferência
Não criptografado
Digest Access Authentication
Aplica hash MD5 nas credenciais
Mais seguro que Basic Auth
OAuthBaseado na criação de “tokens” de
acesso para clientes terceiros
Aplicativos acessando sua conta do Facebook ou CI SAAS acessando um
repositório no GitHub, por exemplo
from requests.auth import HTTPBasicAuth
basic_auth = HTTPBasicAuth('user', 'pass')
requests.get('https://api.github.com/user', auth=basic_auth)
from requests.auth import HTTPDigestAuth
digest_auth = HTTPDigestAuth('user', 'pass')
from requests_oauthlib import OAuth1
oauth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET',
'USER_OAUTH_TOKEN',
'USER_OAUTH_TOKEN_SECRET')
http://docs.python-requests.org/en/latest/user/authentication/
Links e referênciashttps://github.com/chkile/minicurso-octobertest
http://www.w3.org/Protocols/
http://docs.python-requests.org/en/latest/
https://www.python.org/dev/peps/pep-0008/