Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added HTTP authentication to the API. #115

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions atest/auth.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
*** Settings ***
Library REST

*** Variables ***
${baseurl} = https://postman-echo.com
${user} = postman
${password}= password

*** Test Cases ***
GET Basic Auth
${url} Set Variable ${baseurl}/basic-auth

Set Client Authentication basic ${user} ${password}
GET ${url}
Integer response status 200

Set Client Authentication digest ${user} ${password}
Run Keyword And Expect Error KeyError: 'content-type'
... GET ${url}
Integer response status 401

Set Client Authentication ${NONE}
Run Keyword And Expect Error KeyError: 'content-type'
... GET ${url}
Integer response status 401

GET Digest Auth
${url} Set Variable ${baseurl}/digest-auth

Set Client Authentication digest ${user} ${password}
GET ${url}
Integer response status 200

Set Client Authentication basic ${user} ${password}
Run Keyword And Expect Error KeyError: 'content-type'
... GET ${url}
Integer response status 401

Set Client Authentication ${NONE}
Run Keyword And Expect Error KeyError: 'content-type'
... GET ${url}
Integer response status 401

Set Auth Invalid Args
${error} Set Variable TypeError: Argument "auth_type" must be \${NONE}, basic, digest or proxy.
Run Keyword And Expect Error ${error}
... Set Client Authentication none ${user} ${password}

Run Keyword And Expect Error ${error}
... Set Client Authentication doge ${user} ${password}

Run Keyword And Expect Error ${error}
... Set Client Authentication ${1} ${user} ${password}
1 change: 1 addition & 0 deletions src/REST/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def __init__(
"query": {},
"body": None,
"data": None,
"auth" : None,
"headers": {
"Accept": REST._input_string(accept),
"Content-Type": REST._input_string(content_type),
Expand Down
64 changes: 56 additions & 8 deletions src/REST/keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from tzlocal import get_localzone

from collections import OrderedDict
from copy import deepcopy
from copy import deepcopy, copy
from datetime import datetime
from json import dumps, loads
from os import path, getcwd
Expand All @@ -35,6 +35,7 @@
from jsonschema import validate, FormatChecker
from jsonschema.exceptions import SchemaError, ValidationError
from requests import request as client
from requests.auth import HTTPDigestAuth, HTTPBasicAuth, HTTPProxyAuth
from requests.exceptions import SSLError, Timeout

if IS_PYTHON_2:
Expand Down Expand Up @@ -77,6 +78,44 @@ def set_client_cert(self, cert):
self.request["cert"] = self._input_client_cert(cert)
return self.request["cert"]


@keyword(name="Set Client Authentication", tags=("settings",))
def set_client_authentication(self, auth_type, user=None, password=None):
"""*Attaches HTTP basic authentication to the given requests.*

The API by default will not have authentication enabled.

The auth_type argument must be ${NONE}, basic, digest or proxy.
In case the user sets the auth_type to ${NONE} the authentication
information
The user and password will be written in plain text.

*Examples*

| `Set Client Authentication` | basic | admin | password |
| `Set Client Authentication` | digest | admin | password |
| `Set Client Authentication` | proxy | admin | password |
| `Set Client Authentication` | ${NONE} | | |
"""
error_auth = "Argument \"auth_type\" must be ${NONE}, basic, digest or proxy."
if auth_type != None:
asimell marked this conversation as resolved.
Show resolved Hide resolved
if not isinstance(auth_type, str):
raise TypeError(error_auth)

auth_type = auth_type.lower()

if not auth_type in ["basic", "digest","proxy"] :
raise TypeError(error_auth)

if auth_type == "basic" :
auth_type = HTTPBasicAuth
elif auth_type == "digest" :
auth_type = HTTPDigestAuth
elif auth_type == "proxy" :
auth_type = HTTPProxyAuth

return self._setauth(auth_type, user, password)

@keyword(name="Set Headers", tags=("settings",))
def set_headers(self, headers):
"""*Sets new request headers or updates the existing.*
Expand Down Expand Up @@ -276,7 +315,7 @@ def head(
| `HEAD` | /users/1 | timeout=0.5 |
"""
endpoint = self._input_string(endpoint)
request = deepcopy(self.request)
request = copy(self.request)
request["method"] = "HEAD"
if allow_redirects is not None:
request["allowRedirects"] = self._input_boolean(allow_redirects)
Expand Down Expand Up @@ -319,7 +358,7 @@ def options(
| `OPTIONS` | /users/1 | allow_redirects=false |
"""
endpoint = self._input_string(endpoint)
request = deepcopy(self.request)
request = copy(self.request)
request["method"] = "OPTIONS"
if allow_redirects is not None:
request["allowRedirects"] = self._input_boolean(allow_redirects)
Expand Down Expand Up @@ -373,7 +412,7 @@ def get(
| `GET` | https://jsonplaceholder.typicode.com/users | headers={ "Authentication": "" } |
"""
endpoint = self._input_string(endpoint)
request = deepcopy(self.request)
request = copy(self.request)
request["method"] = "GET"
request["query"] = OrderedDict()
query_in_url = OrderedDict(parse_qsl(urlparse(endpoint).query))
Expand Down Expand Up @@ -431,7 +470,7 @@ def post(
| `POST` | /users | ${CURDIR}/new_user.json |
"""
endpoint = self._input_string(endpoint)
request = deepcopy(self.request)
request = copy(self.request)
request["method"] = "POST"
request["body"] = self.input(body)
if allow_redirects is not None:
Expand Down Expand Up @@ -483,7 +522,7 @@ def put(
| `PUT` | /users/2 | ${dict} |
"""
endpoint = self._input_string(endpoint)
request = deepcopy(self.request)
request = copy(self.request)
request["method"] = "PUT"
request["body"] = self.input(body)
if allow_redirects is not None:
Expand Down Expand Up @@ -535,7 +574,7 @@ def patch(
| `PATCH` | /users/4 | ${dict} |
"""
endpoint = self._input_string(endpoint)
request = deepcopy(self.request)
request = copy(self.request)
request["method"] = "PATCH"
request["body"] = self.input(body)
if allow_redirects is not None:
Expand Down Expand Up @@ -585,7 +624,7 @@ def delete(
| `DELETE` | /users/6/pets | {"name" : "Rex","tagID" : "1234"} |
"""
endpoint = self._input_string(endpoint)
request = deepcopy(self.request)
request = copy(self.request)
request["method"] = "DELETE"
request["body"]=self.input(body)
if allow_redirects is not None:
Expand Down Expand Up @@ -1282,6 +1321,7 @@ def rest_instances(self, file_path=None, sort_keys=False):
self.instances,
ensure_ascii=False,
indent=4,
default=vars,
separators=(",", ": "),
sort_keys=sort_keys,
)
Expand Down Expand Up @@ -1315,6 +1355,13 @@ def set_ssl_verify(self, ssl_verify=True):

### Internal methods

def _setauth(self, auth_type, user = None, password = None):
if auth_type == None:
self.request["auth"] = None
else :
self.request["auth"] = auth_type(user, password)
return self.request["auth"]

def _request(self, endpoint, request, validate=True):
if not endpoint.startswith(("http://", "https://")):
base_url = self.request["scheme"] + "://" + self.request["netloc"]
Expand All @@ -1336,6 +1383,7 @@ def _request(self, endpoint, request, validate=True):
headers=request["headers"],
proxies=request["proxies"],
cert=request["cert"],
auth=request["auth"],
timeout=tuple(request["timeout"]),
allow_redirects=request["allowRedirects"],
verify=request["sslVerify"],
Expand Down