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

add basic authentication #51

Closed
wants to merge 3 commits into from
Closed
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
4 changes: 3 additions & 1 deletion launch/roswww.launch
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
<arg name="name" default="roswww"/>
<arg name="webpath" default="www"/> <!-- package webroot -->
<arg name="cached" default="true"/>
<arg name="basic" default="false"/>

<arg name="port" default="8085"/>
<arg name="start_port" default="$(arg port)" />
<arg name="end_port" default="$(arg port)" />

<node pkg="roswww" type="webserver.py" name="$(arg name)"
args="--name $(arg name) --webpath $(arg webpath) --cached $(arg cached)
--port $(arg port) --start_port $(arg start_port) --end_port $(arg end_port)" />
--basic $(arg basic) --port $(arg port)
--start_port $(arg start_port) --end_port $(arg end_port)" />
</launch>
10 changes: 7 additions & 3 deletions script/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,21 @@ def parse_argument(argv):
parser.add_argument('-p', '--port', default=80, type=int, help='Webserver Port number')
parser.add_argument('-w', '--webpath', default='www', help='package relative path to web pages')
parser.add_argument('--cached', default='true', help='static file is cached')
parser.add_argument('--basic', default='false', help='enable basic authentication')
parser.add_argument('--basic-yaml', default=None, help='basic key yaml file path')
parser.add_argument('--start_port', default=8000, type=int, help='setting up port scan range')
parser.add_argument('--end_port', default=9000, type=int, help='setting up port scan range')

parsed_args = parser.parse_args(argv)
cached = False if parsed_args.cached in [0, False, 'false', 'False'] else True
return parsed_args.name, parsed_args.webpath, (parsed_args.port, parsed_args.start_port, parsed_args.end_port), cached
basic = True if parsed_args.basic in [1, True, 'true', 'True'] else False
basic_yaml = parsed_args.basic_yaml
return parsed_args.name, parsed_args.webpath, (parsed_args.port, parsed_args.start_port, parsed_args.end_port), cached, basic, basic_yaml


if __name__ == '__main__':
rospy.init_node("webserver", disable_signals=True)
name, webpath, port, cached = parse_argument(rospy.myargv()[1:])
webserver = roswww.ROSWWWServer(name, webpath, port, cached)
name, webpath, port, cached, basic, basic_yaml = parse_argument(rospy.myargv()[1:])
webserver = roswww.ROSWWWServer(name, webpath, port, cached, basic, basic_yaml)
webserver.loginfo("Initialised")
webserver.spin()
68 changes: 66 additions & 2 deletions src/roswww/roswww_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,48 @@

import logging

import base64
import functools
import socket
import tornado.ioloop # rosbridge installs tornado
import tornado.web
import yaml

from .webrequest_handler import WebRequestHandler
from .utils import run_shellcommand, split_words, get_packages


def basic_auth(auth):
def decore(f):
def _request_auth(handler):
handler.set_header('WWW-Authenticate', 'Basic realm=roswww')
handler.set_status(401)
handler.finish()
return False

@functools.wraps(f)
def new_f(*args):
handler = args[0]
auth_header = handler.request.headers.get('Authorization')
if auth_header is None:
return _request_auth(handler)
if not auth_header.startswith('Basic '):
return _request_auth(handler)

auth_decoded = base64.decodestring(auth_header.split(' ', 1)[1])
username, password = auth_decoded.split(':', 1)

if auth(username, password):
f(*args)
else:
_request_auth(handler)
return new_f
return decore


class ROSWWWServer():

def __init__(self, name, webpath, ports, cached):
def __init__(self, name, webpath, ports, cached, basic=False, basic_yaml=None):
'''
:param str name: webserver name
:param str webpath: package relative path to web page source.
Expand All @@ -54,20 +87,51 @@ def __init__(self, name, webpath, ports, cached):
self._webpath = webpath
self._ports = ports
self._cached = cached
self._basic = basic
self._logger = self._set_logger()
self._packages = get_packages()
self._application = self._create_webserver(self._packages)
if self._basic:
if basic_yaml:
with open(basic_yaml) as f:
self._keys = yaml.safe_load(f)
else:
self._keys = {'admin': 'admin'}

def _create_webserver(self, packages):
'''
@type packages: {str, str}
@param packages: name and path of ROS packages.
'''
def _auth(username, password):
if username in self._keys:
return self._keys[username] == password
return False

class NoCacheStaticFileHandler(tornado.web.StaticFileHandler):
def set_extra_headers(self, path):
self.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')

file_handler = tornado.web.StaticFileHandler if self._cached else NoCacheStaticFileHandler
class BasicStaticFileHandler(tornado.web.StaticFileHandler):
@basic_auth(_auth)
def get(self, path, include_body=True):
super(BasicStaticFileHandler, self).get(path, include_body)

class BasicNoCacheStaticFileHandler(NoCacheStaticFileHandler):
@basic_auth(_auth)
def get(self, path, include_body=True):
super(BasicNoCacheStaticFileHandler, self).get(path, include_body)

if self._cached:
if self._basic:
file_handler = BasicStaticFileHandler
else:
file_handler = tornado.web.StaticFileHandler
else:
if self._basic:
file_handler = BasicNoCacheStaticFileHandler
else:
file_handler = NoCacheStaticFileHandler

handlers = [(r"/", WebRequestHandler, {"packages": packages})]

Expand Down