Revision 27a8a190

b/NEWS
2 2
====
3 3

  
4 4

  
5
Version 2.8.0 beta1
6
-------------------
7

  
8
*(unreleased)*
9

  
10
- The :doc:`Remote API <rapi>` daemon now supports a command line flag
11
  to always require authentication, ``--require-authentication``. It can
12
  be specified in ``$sysconfdir/default/ganeti``.
13

  
14

  
5 15
Version 2.7.0 beta1
6 16
-------------------
7 17

  
b/lib/rapi/testutils.py
210 210
  """Mocking out the RAPI server parts.
211 211

  
212 212
  """
213
  def __init__(self, user_fn, luxi_client):
213
  def __init__(self, user_fn, luxi_client, reqauth=False):
214 214
    """Initialize this class.
215 215

  
216 216
    @type user_fn: callable
......
219 219

  
220 220
    """
221 221
    self.handler = \
222
      server.rapi.RemoteApiHandler(user_fn, _client_cls=luxi_client)
222
      server.rapi.RemoteApiHandler(user_fn, reqauth, _client_cls=luxi_client)
223 223

  
224 224
  def FetchResponse(self, path, method, headers, request_body):
225 225
    """This is a callback method used to fetch a response.
b/lib/server/rapi.py
73 73
  """
74 74
  AUTH_REALM = "Ganeti Remote API"
75 75

  
76
  def __init__(self, user_fn, _client_cls=None):
76
  def __init__(self, user_fn, reqauth, _client_cls=None):
77 77
    """Initializes this class.
78 78

  
79 79
    @type user_fn: callable
80 80
    @param user_fn: Function receiving username as string and returning
81 81
      L{http.auth.PasswordFileUser} or C{None} if user is not found
82
    @type reqauth: bool
83
    @param reqauth: Whether to require authentication
82 84

  
83 85
    """
84 86
    # pylint: disable=W0233
......
88 90
    self._client_cls = _client_cls
89 91
    self._resmap = connector.Mapper()
90 92
    self._user_fn = user_fn
93
    self._reqauth = reqauth
91 94

  
92 95
  @staticmethod
93 96
  def FormatErrorMessage(values):
......
143 146
    """Determine whether authentication is required.
144 147

  
145 148
    """
146
    return bool(self._GetRequestContext(req).handler_access)
149
    return self._reqauth or bool(self._GetRequestContext(req).handler_access)
147 150

  
148 151
  def Authenticate(self, req, username, password):
149 152
    """Checks whether a user can access a resource.
......
324 327

  
325 328
  users = RapiUsers()
326 329

  
327
  handler = RemoteApiHandler(users.Get)
330
  handler = RemoteApiHandler(users.Get, options.reqauth)
328 331

  
329 332
  # Setup file watcher (it'll be driven by asyncore)
330 333
  SetupFileWatcher(pathutils.RAPI_USERS_FILE,
......
360 363
                                 usage="%prog [-f] [-d] [-p port] [-b ADDRESS]",
361 364
                                 version="%%prog (ganeti) %s" %
362 365
                                 constants.RELEASE_VERSION)
366
  parser.add_option("--require-authentication", dest="reqauth",
367
                    default=False, action="store_true",
368
                    help=("Disable anonymous HTTP requests and require"
369
                          " authentication"))
363 370

  
364 371
  daemon.GenericMain(constants.RAPI, parser, CheckRapi, PrepRapi, ExecRapi,
365 372
                     default_ssl_cert=pathutils.RAPI_CERT_FILE,
b/man/ganeti-rapi.rst
9 9
Synopsis
10 10
--------
11 11

  
12
**ganeti-rapi** [-d] [-f] [\--no-ssl] [-K *SSL_KEY_FILE*] [-C
13
*SSL_CERT_FILE*]
12
| **ganeti-rapi** [-d] [-f] [\--no-ssl] [-K *SSL_KEY_FILE*]
13
| [-C *SSL_CERT_FILE*] [\--require-authentication]
14 14

  
15 15
DESCRIPTION
16 16
-----------
......
23 23
``--no-ssl`` option, or alternatively the certificate used can be
24 24
changed via the ``-C`` option and the key via the ``-K`` option.
25 25

  
26
The daemon will listen to the "ganeti-rapi" tcp port, as listed in the
26
The daemon will listen to the "ganeti-rapi" TCP port, as listed in the
27 27
system services database, or if not defined, to port 5080 by default.
28 28

  
29 29
See the *Ganeti remote API* documentation for further information.
......
36 36

  
37 37
Most query operations are allowed without authentication. Only the
38 38
modification operations require authentication, in the form of basic
39
authentication.
39
authentication. Specify the ``--require-authentication`` command line
40
flag to always require authentication.
40 41

  
41 42
The users and their rights are defined in the
42
``@LOCALSTATEDIR@/lib/ganeti/rapi/users`` file. Format of this file is
43
described in the Ganeti documentation (``rapi.html``).
43
``@LOCALSTATEDIR@/lib/ganeti/rapi/users`` file. The format of this file
44
is described in the Ganeti documentation (``rapi.html``).
44 45

  
45 46
.. vim: set textwidth=72 :
46 47
.. Local Variables:
b/test/py/ganeti.server.rapi_unittest.py
51 51
    return None
52 52

  
53 53
  def _Test(self, method, path, headers, reqbody,
54
            user_fn=NotImplemented, luxi_client=NotImplemented):
55
    rm = rapi.testutils._RapiMock(user_fn, luxi_client)
54
            user_fn=NotImplemented, luxi_client=NotImplemented,
55
            reqauth=False):
56
    rm = rapi.testutils._RapiMock(user_fn, luxi_client, reqauth=reqauth)
56 57

  
57 58
    (resp_code, resp_headers, resp_body) = \
58 59
      rm.FetchResponse(path, method, http.ParseHeaders(StringIO(headers)),
......
70 71
    self.assertEqual(code, http.HTTP_OK)
71 72
    self.assertTrue(data is None)
72 73

  
74
  def testRootReqAuth(self):
75
    (code, _, _) = self._Test(http.HTTP_GET, "/", "", None, reqauth=True)
76
    self.assertEqual(code, http.HttpUnauthorized.code)
77

  
73 78
  def testVersion(self):
74 79
    (code, _, data) = self._Test(http.HTTP_GET, "/version", "", None)
75 80
    self.assertEqual(code, http.HTTP_OK)
......
235 240
    path = "/2/instances/inst1.example.com/console"
236 241

  
237 242
    for method in rapi.baserlib._SUPPORTED_METHODS:
238
      # No authorization
239
      (code, _, _) = self._Test(method, path, "", "")
243
      for reqauth in [False, True]:
244
        # No authorization
245
        (code, _, _) = self._Test(method, path, "", "", reqauth=reqauth)
240 246

  
241
      if method == http.HTTP_GET:
242
        self.assertEqual(code, http.HttpUnauthorized.code)
243
      else:
244
        self.assertEqual(code, http.HttpNotImplemented.code)
247
        if method == http.HTTP_GET or reqauth:
248
          self.assertEqual(code, http.HttpUnauthorized.code)
249
        else:
250
          self.assertEqual(code, http.HttpNotImplemented.code)
245 251

  
246 252

  
247 253
class _FakeLuxiClientForQuery:

Also available in: Unified diff