As this functinoality will be used in a PAM module, move it into an
independent module.

Signed-off-by: Oleg Ponomarev <[email protected]>
---
 Makefile.am                            |   3 +-
 lib/http/auth.py                       |  53 -------------
 lib/rapi/testutils.py                  |   5 +-
 lib/rapi/users_file.py                 | 137 +++++++++++++++++++++++++++++++++
 lib/server/rapi.py                     |  52 +------------
 qa/qa_rapi.py                          |   2 +-
 test/py/ganeti.http_unittest.py        |   5 +-
 test/py/ganeti.server.rapi_unittest.py |   7 +-
 8 files changed, 151 insertions(+), 113 deletions(-)
 create mode 100644 lib/rapi/users_file.py

diff --git a/Makefile.am b/Makefile.am
index 3c5c8ee..979f596 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -568,7 +568,8 @@ rapi_PYTHON = \
        lib/rapi/client_utils.py \
        lib/rapi/connector.py \
        lib/rapi/rlib2.py \
-       lib/rapi/testutils.py
+       lib/rapi/testutils.py \
+       lib/rapi/users_file.py
 
 http_PYTHON = \
        lib/http/__init__.py \
diff --git a/lib/http/auth.py b/lib/http/auth.py
index 35b0b32..83f0cba 100644
--- a/lib/http/auth.py
+++ b/lib/http/auth.py
@@ -38,7 +38,6 @@ import binascii
 
 from ganeti import compat
 from ganeti import http
-from ganeti import utils
 
 from cStringIO import StringIO
 
@@ -285,55 +284,3 @@ class HttpServerRequestAuthentication(object):
 
     return False
 
-
-class PasswordFileUser(object):
-  """Data structure for users from password file.
-
-  """
-  def __init__(self, name, password, options):
-    self.name = name
-    self.password = password
-    self.options = options
-
-
-def ParsePasswordFile(contents):
-  """Parses the contents of a password file.
-
-  Lines in the password file are of the following format::
-
-      <username> <password> [options]
-
-  Fields are separated by whitespace. Username and password are mandatory,
-  options are optional and separated by comma (','). Empty lines and comments
-  ('#') are ignored.
-
-  @type contents: str
-  @param contents: Contents of password file
-  @rtype: dict
-  @return: Dictionary containing L{PasswordFileUser} instances
-
-  """
-  users = {}
-
-  for line in utils.FilterEmptyLinesAndComments(contents):
-    parts = line.split(None, 2)
-    if len(parts) < 2:
-      # Invalid line
-      # TODO: Return line number from FilterEmptyLinesAndComments
-      logging.warning("Ignoring non-comment line with less than two fields")
-      continue
-
-    name = parts[0]
-    password = parts[1]
-
-    # Extract options
-    options = []
-    if len(parts) >= 3:
-      for part in parts[2].split(","):
-        options.append(part.strip())
-    else:
-      logging.warning("Ignoring values for user '%s': %s", name, parts[3:])
-
-    users[name] = PasswordFileUser(name, password, options)
-
-  return users
diff --git a/lib/rapi/testutils.py b/lib/rapi/testutils.py
index 8f7a7ad..d4aa04b 100644
--- a/lib/rapi/testutils.py
+++ b/lib/rapi/testutils.py
@@ -50,6 +50,7 @@ from ganeti import rapi
 
 import ganeti.http.server # pylint: disable=W0611
 import ganeti.server.rapi
+from ganeti.rapi import users_file
 import ganeti.rapi.client
 
 
@@ -363,8 +364,8 @@ class InputTestClient(object):
 
       """
       assert username == wanted
-      return http.auth.PasswordFileUser(username, password,
-                                        [rapi.RAPI_ACCESS_WRITE])
+      return users_file.PasswordFileUser(username, password,
+                                         [rapi.RAPI_ACCESS_WRITE])
 
     self._lcr = _LuxiCallRecorder()
 
diff --git a/lib/rapi/users_file.py b/lib/rapi/users_file.py
new file mode 100644
index 0000000..e2a26d3
--- /dev/null
+++ b/lib/rapi/users_file.py
@@ -0,0 +1,137 @@
+#
+#
+
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2015 Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""RAPI users config file parser.
+
+"""
+
+import errno
+import logging
+
+from ganeti import utils
+
+
+class PasswordFileUser(object):
+  """Data structure for users from password file.
+
+  """
+  def __init__(self, name, password, options):
+    self.name = name
+    self.password = password
+    self.options = options
+
+
+def ParsePasswordFile(contents):
+  """Parses the contents of a password file.
+
+  Lines in the password file are of the following format::
+
+      <username> <password> [options]
+
+  Fields are separated by whitespace. Username and password are mandatory,
+  options are optional and separated by comma (','). Empty lines and comments
+  ('#') are ignored.
+
+  @type contents: str
+  @param contents: Contents of password file
+  @rtype: dict
+  @return: Dictionary containing L{PasswordFileUser} instances
+
+  """
+  users = {}
+
+  for line in utils.FilterEmptyLinesAndComments(contents):
+    parts = line.split(None, 2)
+    if len(parts) < 2:
+      # Invalid line
+      # TODO: Return line number from FilterEmptyLinesAndComments
+      logging.warning("Ignoring non-comment line with less than two fields")
+      continue
+
+    name = parts[0]
+    password = parts[1]
+
+    # Extract options
+    options = []
+    if len(parts) >= 3:
+      for part in parts[2].split(","):
+        options.append(part.strip())
+    else:
+      logging.warning("Ignoring values for user '%s': %s", name, parts[3:])
+
+    users[name] = PasswordFileUser(name, password, options)
+
+  return users
+
+
+class RapiUsers(object):
+  def __init__(self):
+    """Initializes this class.
+
+    """
+    self._users = None
+
+  def Get(self, username):
+    """Checks whether a user exists.
+
+    """
+    if self._users:
+      return self._users.get(username, None)
+    else:
+      return None
+
+  def Load(self, filename):
+    """Loads a file containing users and passwords.
+
+    @type filename: string
+    @param filename: Path to file
+
+    """
+    logging.info("Reading users file at %s", filename)
+    try:
+      try:
+        contents = utils.ReadFile(filename)
+      except EnvironmentError, err:
+        self._users = None
+        if err.errno == errno.ENOENT:
+          logging.warning("No users file at %s", filename)
+        else:
+          logging.warning("Error while reading %s: %s", filename, err)
+        return False
+
+      users = ParsePasswordFile(contents)
+
+    except Exception, err: # pylint: disable=W0703
+      # We don't care about the type of exception
+      logging.error("Error while parsing %s: %s", filename, err)
+      return False
+
+    self._users = users
+
+    return True
diff --git a/lib/server/rapi.py b/lib/server/rapi.py
index 9782ada..2fd61f7 100644
--- a/lib/server/rapi.py
+++ b/lib/server/rapi.py
@@ -40,7 +40,6 @@ import optparse
 import sys
 import os
 import os.path
-import errno
 
 try:
   from pyinotify import pyinotify # pylint: disable=E0611
@@ -55,10 +54,10 @@ from ganeti import ssconf
 import ganeti.rpc.errors as rpcerr
 from ganeti import serializer
 from ganeti import compat
-from ganeti import utils
 from ganeti import pathutils
 from ganeti.rapi import connector
 from ganeti.rapi import baserlib
+from ganeti.rapi import users_file
 
 import ganeti.http.auth   # pylint: disable=W0611
 import ganeti.http.server
@@ -214,53 +213,6 @@ class 
RemoteApiHandler(http.auth.HttpServerRequestAuthentication,
     return serializer.DumpJson(result)
 
 
-class RapiUsers(object):
-  def __init__(self):
-    """Initializes this class.
-
-    """
-    self._users = None
-
-  def Get(self, username):
-    """Checks whether a user exists.
-
-    """
-    if self._users:
-      return self._users.get(username, None)
-    else:
-      return None
-
-  def Load(self, filename):
-    """Loads a file containing users and passwords.
-
-    @type filename: string
-    @param filename: Path to file
-
-    """
-    logging.info("Reading users file at %s", filename)
-    try:
-      try:
-        contents = utils.ReadFile(filename)
-      except EnvironmentError, err:
-        self._users = None
-        if err.errno == errno.ENOENT:
-          logging.warning("No users file at %s", filename)
-        else:
-          logging.warning("Error while reading %s: %s", filename, err)
-        return False
-
-      users = http.auth.ParsePasswordFile(contents)
-
-    except Exception, err: # pylint: disable=W0703
-      # We don't care about the type of exception
-      logging.error("Error while parsing %s: %s", filename, err)
-      return False
-
-    self._users = users
-
-    return True
-
-
 class FileEventHandler(asyncnotifier.FileEventHandlerBase):
   def __init__(self, wm, path, cb):
     """Initializes this class.
@@ -334,7 +286,7 @@ def PrepRapi(options, _):
   """
   mainloop = daemon.Mainloop()
 
-  users = RapiUsers()
+  users = users_file.RapiUsers()
 
   handler = RemoteApiHandler(users.Get, options.reqauth)
 
diff --git a/qa/qa_rapi.py b/qa/qa_rapi.py
index d997c24..df3edcb 100644
--- a/qa/qa_rapi.py
+++ b/qa/qa_rapi.py
@@ -54,7 +54,7 @@ from ganeti import query
 from ganeti import rapi
 from ganeti import utils
 
-from ganeti.http.auth import ParsePasswordFile
+from ganeti.rapi.users_file import ParsePasswordFile
 import ganeti.rapi.client        # pylint: disable=W0611
 import ganeti.rapi.client_utils
 
diff --git a/test/py/ganeti.http_unittest.py b/test/py/ganeti.http_unittest.py
index 518f817..c713395 100755
--- a/test/py/ganeti.http_unittest.py
+++ b/test/py/ganeti.http_unittest.py
@@ -42,6 +42,7 @@ from cStringIO import StringIO
 
 from ganeti import http
 from ganeti import compat
+from ganeti.rapi import users_file
 
 import ganeti.http.server
 import ganeti.http.client
@@ -306,7 +307,7 @@ class 
TestHttpServerRequestAuthentication(unittest.TestCase):
 
 class TestReadPasswordFile(unittest.TestCase):
   def testSimple(self):
-    users = http.auth.ParsePasswordFile("user1 password")
+    users = users_file.ParsePasswordFile("user1 password")
     self.assertEqual(len(users), 1)
     self.assertEqual(users["user1"].password, "password")
     self.assertEqual(len(users["user1"].options), 0)
@@ -321,7 +322,7 @@ class TestReadPasswordFile(unittest.TestCase):
     buf.write("   \t# Another comment\n")
     buf.write("invalidline\n")
 
-    users = http.auth.ParsePasswordFile(buf.getvalue())
+    users = users_file.ParsePasswordFile(buf.getvalue())
     self.assertEqual(len(users), 2)
     self.assertEqual(users["user1"].password, "password")
     self.assertEqual(len(users["user1"].options), 0)
diff --git a/test/py/ganeti.server.rapi_unittest.py 
b/test/py/ganeti.server.rapi_unittest.py
index ee879bd..1bdda1d 100755
--- a/test/py/ganeti.server.rapi_unittest.py
+++ b/test/py/ganeti.server.rapi_unittest.py
@@ -47,6 +47,7 @@ from ganeti import http
 from ganeti import objects
 
 import ganeti.rapi.baserlib
+from ganeti.rapi import users_file
 import ganeti.rapi.testutils
 import ganeti.rapi.rlib2
 import ganeti.http.auth
@@ -178,16 +179,14 @@ class TestRemoteApiHandler(unittest.TestCase):
 
     def _LookupUserNoWrite(name):
       if name == username:
-        return http.auth.PasswordFileUser(name, password, [])
+        return users_file.PasswordFileUser(name, password, [])
       else:
         return None
 
     for access in [rapi.RAPI_ACCESS_WRITE, rapi.RAPI_ACCESS_READ]:
       def _LookupUserWithWrite(name):
         if name == username:
-          return http.auth.PasswordFileUser(name, password, [
-            access,
-            ])
+          return users_file.PasswordFileUser(name, password, [access])
         else:
           return None
 
-- 
2.6.0.rc2.230.g3dd15c0

Reply via email to