Revision abe318b3 lib/server/rapi.py

b/lib/server/rapi.py
71 71
  """
72 72
  AUTH_REALM = "Ganeti Remote API"
73 73

  
74
  def __init__(self, _client_cls=None):
74
  def __init__(self, user_fn, _client_cls=None):
75
    """Initializes this class.
76

  
77
    @type user_fn: callable
78
    @param user_fn: Function receiving username as string and returning
79
      L{http.auth.PasswordFileUser} or C{None} if user is not found
80

  
81
    """
75 82
    # pylint: disable=W0233
76 83
    # it seems pylint doesn't see the second parent class there
77 84
    http.server.HttpServerHandler.__init__(self)
78 85
    http.auth.HttpServerRequestAuthentication.__init__(self)
79 86
    self._client_cls = _client_cls
80 87
    self._resmap = connector.Mapper()
81
    self._users = None
82

  
83
  def LoadUsers(self, filename):
84
    """Loads a file containing users and passwords.
85

  
86
    @type filename: string
87
    @param filename: Path to file
88

  
89
    """
90
    logging.info("Reading users file at %s", filename)
91
    try:
92
      try:
93
        contents = utils.ReadFile(filename)
94
      except EnvironmentError, err:
95
        self._users = None
96
        if err.errno == errno.ENOENT:
97
          logging.warning("No users file at %s", filename)
98
        else:
99
          logging.warning("Error while reading %s: %s", filename, err)
100
        return False
101

  
102
      users = http.auth.ParsePasswordFile(contents)
103

  
104
    except Exception, err: # pylint: disable=W0703
105
      # We don't care about the type of exception
106
      logging.error("Error while parsing %s: %s", filename, err)
107
      return False
108

  
109
    self._users = users
110

  
111
    return True
88
    self._user_fn = user_fn
112 89

  
113 90
  @staticmethod
114 91
  def FormatErrorMessage(values):
......
172 149
    """
173 150
    ctx = self._GetRequestContext(req)
174 151

  
175
    # Check username and password
176
    valid_user = False
177
    if self._users:
178
      user = self._users.get(username, None)
179
      if user and self.VerifyBasicAuthPassword(req, username, password,
180
                                               user.password):
181
        valid_user = True
182

  
183
    if not valid_user:
152
    user = self._user_fn(username)
153
    if not (user and
154
            self.VerifyBasicAuthPassword(req, username, password,
155
                                         user.password)):
184 156
      # Unknown user or password wrong
185 157
      return False
186 158

  
......
232 204
    return serializer.DumpJson(result)
233 205

  
234 206

  
207
class RapiUsers:
208
  def __init__(self):
209
    """Initializes this class.
210

  
211
    """
212
    self._users = None
213

  
214
  def Get(self, username):
215
    """Checks whether a user exists.
216

  
217
    """
218
    if self._users:
219
      return self._users.get(username, None)
220
    else:
221
      return None
222

  
223
  def Load(self, filename):
224
    """Loads a file containing users and passwords.
225

  
226
    @type filename: string
227
    @param filename: Path to file
228

  
229
    """
230
    logging.info("Reading users file at %s", filename)
231
    try:
232
      try:
233
        contents = utils.ReadFile(filename)
234
      except EnvironmentError, err:
235
        self._users = None
236
        if err.errno == errno.ENOENT:
237
          logging.warning("No users file at %s", filename)
238
        else:
239
          logging.warning("Error while reading %s: %s", filename, err)
240
        return False
241

  
242
      users = http.auth.ParsePasswordFile(contents)
243

  
244
    except Exception, err: # pylint: disable=W0703
245
      # We don't care about the type of exception
246
      logging.error("Error while parsing %s: %s", filename, err)
247
      return False
248

  
249
    self._users = users
250

  
251
    return True
252

  
253

  
235 254
class FileEventHandler(asyncnotifier.FileEventHandlerBase):
236 255
  def __init__(self, wm, path, cb):
237 256
    """Initializes this class.
......
304 323

  
305 324
  """
306 325
  mainloop = daemon.Mainloop()
307
  handler = RemoteApiHandler()
326

  
327
  users = RapiUsers()
328

  
329
  handler = RemoteApiHandler(users.Get)
308 330

  
309 331
  # Setup file watcher (it'll be driven by asyncore)
310 332
  SetupFileWatcher(constants.RAPI_USERS_FILE,
311
                   compat.partial(handler.LoadUsers, constants.RAPI_USERS_FILE))
333
                   compat.partial(users.Load, constants.RAPI_USERS_FILE))
312 334

  
313
  handler.LoadUsers(constants.RAPI_USERS_FILE)
335
  users.Load(constants.RAPI_USERS_FILE)
314 336

  
315 337
  server = \
316 338
    http.server.HttpServer(mainloop, options.bind_address, options.port,

Also available in: Unified diff