]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
restful: Use keys instead of tokens+cephx
authorBoris Ranto <branto@redhat.com>
Tue, 25 Apr 2017 01:10:04 +0000 (18:10 -0700)
committerBoris Ranto <branto@redhat.com>
Mon, 22 May 2017 17:19:02 +0000 (19:19 +0200)
Signed-off-by: Boris Ranto <branto@redhat.com>
src/pybind/mgr/restful/api.py
src/pybind/mgr/restful/module.py

index 9cceccb24c2c06bc5cd3a3a6124696d6c31ce013..3c09ad6a42753702b962f35e657e345f18d3934c 100644 (file)
@@ -39,14 +39,17 @@ def auth(f):
 
         username, password = b64decode(request.authorization[1]).split(':')
 
-        # Lookup the password-less tokens first
-        if username not in module.instance.tokens.values():
-            # Check the ceph auth db
-            msg = module.instance.verify_user(username, password)
-            if msg:
-                response.status = 401
-                response.headers['WWW-Authenticate'] = 'Basic realm="Login Required"'
-                return {'message': 'auth: No HTTP username/password'}
+        # Check that the username exists
+        if username not in module.instance.keys:
+            response.status = 401
+            response.headers['WWW-Authenticate'] = 'Basic realm="Login Required"'
+            return {'message': 'auth: No such user'}
+
+        # Check the password
+        if module.instance.keys[username] != password:
+            response.status = 401
+            response.headers['WWW-Authenticate'] = 'Basic realm="Login Required"'
+            return {'message': 'auth: Incorrect password'}
 
         return f(*args, **kwargs)
     return decorated
@@ -637,64 +640,7 @@ class Config(RestController):
 
 
 
-class Auth(RestController):
-    @expose('json')
-    @catch
-    def get(self):
-        """
-        Generate a brand new password-less login token for the user
-        Uses HTTP Basic Auth for authentication
-        """
-        if not request.authorization:
-            return (
-                {'message': 'auth: No HTTP username/password'},
-                401,
-                {'WWW-Authenticate': 'Basic realm="Login Required"'}
-            )
-
-        username, password = b64decode(request.authorization[1]).split(':')
-        # Do not create a new token for a username that is already a token
-        if username in module.instance.tokens.values():
-            return {
-                'token': username
-            }
-
-        # Check the ceph auth db
-        msg = module.instance.verify_user(username, password)
-        if msg:
-            return (
-                {'message': 'auth: ' + msg},
-                401,
-                {'WWW-Authenticate': 'Basic realm="Login Required"'}
-            )
-
-        # Create a password-less login token for the user
-        # This overwrites any old user tokens
-        return {
-            'token': module.instance.set_token(username)
-        }
-
-
-    @expose('json')
-    @catch
-    @auth
-    def delete(self):
-        """
-        Delete the password-less login token for the user
-        """
-
-        username, password = b64decode(request.authorization[1]).split(':')
-
-        if module.instance.unset_token(username):
-            return {'success': 'auth: Token removed'}
-
-        response.status = 500
-        return {'message': 'auth: No token for the user'}
-
-
-
 class Root(RestController):
-    auth = Auth()
     config = Config()
     crush = Crush()
     doc = Doc()
index ddfff9715f404196c89f326bac9a76587478eb93..ba99cefcbea6c414ec4487248ece1fe8ca391a7b 100644 (file)
@@ -3,6 +3,7 @@ A RESTful API for Ceph
 """
 
 import json
+import errno
 import inspect
 import StringIO
 import threading
@@ -179,7 +180,25 @@ class CommandsRequest(object):
 
 
 class Module(MgrModule):
-    COMMANDS = []
+    COMMANDS = [
+            {
+                "cmd": "create_key "
+                       "name=key_name,type=CephString",
+                "desc": "Create an API key with this name",
+                "perm": "rw"
+            },
+            {
+                "cmd": "delete_key "
+                       "name=key_name,type=CephString",
+                "desc": "Delete an API key with this name",
+                "perm": "rw"
+            },
+            {
+                "cmd": "list_keys",
+                "desc": "List all API keys",
+                "perm": "rw"
+            },
+    ]
 
     def __init__(self, *args, **kwargs):
         super(Module, self).__init__(*args, **kwargs)
@@ -189,7 +208,7 @@ class Module(MgrModule):
         self.requests = []
         self.requests_lock = threading.RLock()
 
-        self.tokens = {}
+        self.keys = {}
         self.disable_auth = False
 
         self.shutdown_key = str(uuid4())
@@ -205,8 +224,8 @@ class Module(MgrModule):
 
 
     def _serve(self):
-        # Load stored authentication tokens
-        self.tokens = self.get_config_json("tokens") or {}
+        # Load stored authentication keys
+        self.keys = self.get_config_json("keys") or {}
 
         jsonify._instance = jsonify.GenericJSON(
             sort_keys=True,
@@ -279,6 +298,48 @@ class Module(MgrModule):
             self.log.debug("Unhandled notification type '%s'" % notify_type)
 
 
+    def handle_command(self, command):
+        self.log.warn("Handling command: '%s'" % str(command))
+        if command['prefix'] == "create_key":
+            if command['key_name'] in self.keys:
+                return 0, self.keys[command['key_name']], ""
+
+            else:
+                self.keys[command['key_name']] = str(uuid4())
+                self.set_config_json('keys', self.keys)
+
+            return (
+                0,
+                self.keys[command['key_name']],
+                "",
+            )
+
+        elif command['prefix'] == "delete_key":
+            if command['key_name'] in self.keys:
+                del self.keys[command['key_name']]
+                self.set_config_json('keys', self.keys)
+
+            return (
+                0,
+                "",
+                "",
+            )
+
+        elif command['prefix'] == "list_keys":
+            return (
+                0,
+                json.dumps(self.get_config_json('keys'), indent=2),
+                "",
+            )
+
+        else:
+            return (
+                -errno.EINVAL,
+                "",
+                "Command not found '{0}'".format(prefix)
+            )
+
+
     def create_cert(self):
         # create a key pair
         pkey = crypto.PKey()
@@ -445,56 +506,3 @@ class Module(MgrModule):
 
         self.send_command(result, json.dumps(command), 'seq')
         return result.wait()
-
-
-    def verify_user(self, username, password):
-        r, outb, outs = self.run_command({
-            'prefix': 'auth get',
-            'entity': username,
-        })
-
-        if r != 0:
-            return 'No such user/key (%s, %s)' % (outb, outs)
-
-        ## check the capabilities, we are looking for mon allow *
-        conf = ConfigParser.ConfigParser()
-
-        # ConfigParser can't handle tabs, remove them
-        conf.readfp(StringIO.StringIO(outb.replace('\t', '')))
-
-        if not conf.has_section(username):
-            return 'Failed to parse the auth details'
-
-        key = conf.get(username, 'key')
-
-        if password != key:
-            return 'Invalid key'
-
-        if not conf.has_option(username, 'caps mon'):
-            return 'No mon caps set'
-
-        caps = conf.get(username, 'caps mon')
-
-        if caps not in ['"allow *"', '"allow profile mgr"']:
-            return 'Insufficient mon caps set'
-
-        # Returning '' means 'no objections'
-        return ''
-
-
-    def set_token(self, user):
-        self.tokens[user] = str(uuid4())
-
-        self.set_config_json('tokens', self.tokens)
-
-        return self.tokens[user]
-
-
-    def unset_token(self, user):
-        if user not in self.tokens:
-            return False
-
-        del self.tokens[user]
-        self.set_config_json('tokens', self.tokens)
-
-        return True