Skip to content

Instantly share code, notes, and snippets.

@greggomann
Last active June 22, 2016 22:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save greggomann/1da971285c5a570a3fd6e024f90ba464 to your computer and use it in GitHub Desktop.
Save greggomann/1da971285c5a570a3fd6e024f90ba464 to your computer and use it in GitHub Desktop.
Tests Mesos HTTP authorization
#!/usr/bin/env python3
#
# Usage: ./test-authz.py --master=[master_ip] --agent=[agent_ip] --good-jwt=[JWT] --bad-jwt=[JWT]
import requests
import sys
# These will be used to populate the mustache variables in 'config.py'.
# Parameters under 'all' will be used for all remotes; parameters within other
# keys are used for their respective remotes only.
# TODO: others needed: agent_id, framework_id
params = {}
params['all'] = {}
params['master'] = {}
params['agent'] = {}
params['all']['operator_principal'] = 'test_user'
params['all']['role'] = 'test_role'
# Load the dictionary of authorized HTTP endpoints.
authorized_endpoints = {
# Default endpoints:
"/files/debug": {"process": ["master", "agent"]},
"/logging/toggle": {"process": ["master", "agent"]},
"/metrics/snapshot": {"process": ["master", "agent"]},
"/flags": {"process": ["master", "agent"]},
"/frameworks": {"process": ["master"]},
"/quota": {"process": ["master"]},
"/roles": {"process": ["master"]},
"/slaves": {"process": ["master"]},
"/state": {"process": ["master"]},
"/state-summary": {"process": ["master"]},
"/tasks": {"process": ["master"]},
"/weights": {"process": ["master"]},
# Non-Default endpoints:
# "/files/browse": {
# "method": "get",
# "body": "path={{ file_path }}",
# "process": ["master", "agent"]
# },
#
# "/files/download": {
# "method": "get",
# "body": "path={{ file_path }}",
# "process": ["master", "agent"]
# },
#
# "/files/read": {
# "method": "get",
# "body": "path={{ file_path }}",
# "process": ["master", "agent"]
# },
#
# "/create-volumes": {
# "method": "post",
# "body": '''
#slaveId={{ agent_id }}&volumes='[
# {
# "name": "disk",
# "type": "SCALAR",
# "scalar": { "value": 512 },
# "role": "{{ role }}",
# "reservation": {
# "principal": "{{ operator_principal }}"
# },
# "disk": {
# "persistence": {
# "id" : "persistent-volume-01",
# "principal" : "{{ operator_principal }}"
# },
# "volume": {
# "mode": "RW",
# "container_path": "volume"
# }
# }
# }
#]
#''',
# "process": ["master"]
# },
#
# "/destroy-volumes": {
# "method": "post",
# "body": '''
#slaveId={{ agent_id }}&volumes='[
# {
# "name": "disk",
# "type": "SCALAR",
# "scalar": { "value": 512 },
# "role": "{{ role }}",
# "reservation": {
# "principal": "{{ operator_principal }}"
# },
# "disk": {
# "persistence": {
# "id" : "persistent-volume-01"
# },
# "volume": {
# "mode": "RW",
# "container_path": "volume"
# }
# }
# }
#]
#''',
# "process": ["master"]
# },
#
# "/reserve": {
# "method": "post",
# "body": '''
#slaveId={{ slave_id }}&resources='[
# {
# "name": "cpus",
# "type": "SCALAR",
# "scalar": { "value": 1 },
# "role": "{{ role }}",
# "reservation": {
# "principal": "{{ operator_principal }}"
# }
# }
#]
#''',
# "process": ["master"]
# },
# "/unreserve": {
# "method": "post",
# "body": '''
#slaveId={{ slave_id }}&resources='[
# {
# "name": "cpus",
# "type": "SCALAR",
# "scalar": { "value": 1 },
# "role": "{{ role }}",
# "reservation": {
# "principal": "{{ operator_principal }}"
# }
# }
#]
#''',
# "process": ["master"]
# },
# "/teardown": {
# "method": "post",
# "body": "frameworkId={{ framework_id }}",
# "process": ["master"]
# }
}
# Substitute for moustache variables.
def parse_body(body, params):
idx_counter = 0
result = ''
idx_begin = body.find('{{', idx_counter)
while idx_begin > 0:
result += body[idx_counter:idx_begin]
idx_end = body.find('}}', idx_begin)
idx_counter = idx_end + 2
key = body[idx_begin:idx_end].strip('{} ')
if key not in params:
sys.exit('Unexpected moustache variable: ' + key)
result += params[key]
result += body[idx_counter:]
return result
remotes = {}
# Grab command-line arguments.
for argument in sys.argv[1:]:
trimmed = argument.strip('--')
arg_array = trimmed.split('=')
if arg_array[0] == 'master':
remotes['master'] = arg_array[1]
elif arg_array[0] == 'agent':
remotes['agent'] = arg_array[1]
elif arg_array[0] == 'good-jwt':
good_jwt = arg_array[1]
elif arg_array[0] == 'bad-jwt':
bad_jwt = arg_array[1]
superuser_auth_header = {'Authorization': good_jwt}
regular_auth_header = {'Authorization': bad_jwt}
for superuser in [True, False]:
headers = superuser_auth_header if superuser else regular_auth_header
for path, http_params in authorized_endpoints.items():
http_method = http_params['method'] if 'method' in http_params else 'get'
for remote in http_params['process']:
http_body = parse_body(http_params['body'], params[remote] + params['all']) if 'body' in http_params else None
if http_method == 'get':
r = requests.get(remotes[remote] + path, http_body)
elif http_method == 'post':
r = requests.post(remotes[remote] + path, json=http_body, headers=headers)
else:
sys.exit('Unexpected HTTP request method: ' + http_method)
if superuser:
assert r.ok, 'ERROR: Authorized request for "' + remotes[remote] + path + '" returned code ' + str(r.status_code) + ' instead of 2XX'
print('OK - Authorized request to "' + remotes[remote] + path + '" returned OK as expected.')
else:
assert r.status_code == 403, 'ERROR: Unauthorized request for "' + remotes[remote] + path + '" returned code ' + str(r.status_code) + ' instead of 403'
print('OK - Unauthorized request to "' + remotes[remote] + path + '" returned 403 as expected.')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment