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
-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()
"""
import json
+import errno
import inspect
import StringIO
import threading
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)
self.requests = []
self.requests_lock = threading.RLock()
- self.tokens = {}
+ self.keys = {}
self.disable_auth = False
self.shutdown_key = str(uuid4())
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,
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()
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