1# Copyright 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Common Utility Methods"""
6
7import cherrypy
8import json
9import logging
10
11import common
12from fake_device_server import server_errors
13
14
15def parse_serialized_json():
16    """Parses incoming cherrypy request as a json."""
17    body_length = int(cherrypy.request.headers.get('Content-Length', 0))
18    data = cherrypy.request.rfile.read(body_length)
19    return json.loads(data) if data else None
20
21
22def grab_header_field(header_name):
23    """Returns the header |header_name| from an incoming request.
24
25    @param header_name: Header name to retrieve.
26    """
27    return cherrypy.request.headers.get(header_name, None)
28
29
30def get_access_token():
31    """Returns the access token from an incoming request.
32
33    @return string access token or None.
34
35    """
36    header = grab_header_field('Authorization')
37    if header is None:
38        logging.error('No authorization header found.')
39        return None
40    fields = header.split()
41    if len(fields) != 2 or fields[0] != "Bearer":
42        logging.error('No access token found.')
43        return None
44    logging.debug('Got authorization header "%s"', header)
45    return fields[1]
46
47
48def parse_common_args(args_tuple, kwargs, supported_operations=set()):
49    """Common method to parse args to a CherryPy RPC for this server.
50
51    |args_tuple| should contain all the sections of the URL after CherryPy
52    removes the pieces that dispatched the URL to this handler. For instance,
53    a GET method receiving '...'/<id>/<method_name> should call:
54    parse_common_args(args_tuple=[<id>, <method_name>]).
55    Some operations take no arguments. Other operations take
56    a single argument. Still other operations take
57    one of supported_operations as a second argument (in the args_tuple).
58
59    @param args_tuple: Tuple of positional args.
60    @param kwargs: Dictionary of named args passed in.
61    @param supported_operations: Set of operations to support if any.
62
63    Returns:
64        A 3-tuple containing the id parsed from the args_tuple, api_key,
65        and finally an optional operation if supported_operations is provided
66        and args_tuple contains one of the supported ops.
67
68    Raises:
69        server_error.HTTPError if combination or args/kwargs doesn't make
70        sense.
71    """
72    args = list(args_tuple)
73    api_key = kwargs.get('key')
74    id = args.pop(0) if args else None
75    operation = args.pop(0) if args else None
76    if operation:
77        if not supported_operations:
78            raise server_errors.HTTPError(
79                    400, 'Received operation when operation was not '
80                    'expected: %s!' % operation)
81        elif not operation in supported_operations:
82            raise server_errors.HTTPError(
83                    400, 'Unsupported operation: %s' % operation)
84
85    # All expected args should be popped off already.
86    if args:
87        raise server_errors.HTTPError(
88                400, 'Could not parse all args: %s' % args)
89
90    return id, api_key, operation
91