major rewrite of the auth function

This commit is contained in:
deflax 2016-03-31 17:40:40 +03:00
parent 20052a773d
commit 1f405f7990
4 changed files with 89 additions and 56 deletions

View file

@ -16,23 +16,28 @@ def addclient(vmid, vmname, clientid, clientname, clientemail, vmpass):
clientsdb = readclientsdb()
if str(clientid) in clientsdb:
ioconfig.logger.info('clients> client ' + clientid + ' already exists. merging.')
ioconfig.logger.info('client[{}]> already exist. merging.'.format(clientid))
else:
ioconfig.logger.info('clients> client ' + clientid + ' does not exist. creating.')
vcard = { 'name':str(clientname), 'email':str(clientemail) }
ioconfig.logger.info('client[{}]> does not exist. creating...'.format(clientid))
#generate password and send it to the client
newpass = utils.genpassword(30)
salt = bcrypt.gensalt()
b_newpass = newpass.encode('utf-8')
encpasswd = bcrypt.hashpw(b_newpass, salt).decode('utf-8')
vcard = { 'name':str(clientname), 'email':str(clientemail), 'encpasswd':str(encpasswd) }
newclient = { str(clientid):vcard }
clientsdb.update(newclient)
ioconfig.logger.info('clients> vmid {} owner set to {} (id: {}, email: {})'.format(vmid, clientname, clientid, clientemail))
#TODO: 1. Send initial email to the user as we wont use the internal auth from now on.
#TODO: 2. Sync with proxmaster-admin database (shell command could be used for this one)
ioconfig.logger.info('client[{}]> vmid {} is now owned by {} ({})'.format(clientemail, vmid, clientid, clientname))
#create initial vm template
vmdata = { 'hostname':str(vmname), 'vmid':str(vmid), 'ownerid':str(clientid) }
clientsdb[str(clientid)][str(vmid)] = vmdata
writeclientsdb(clientsdb)
#set password for the first time...
setencpasswd(vmname, vmpass)
def setencpasswd(vmname, newpass):
def setencpasswd(clientemail, newpass):
""" setup a new management password """
salt = bcrypt.gensalt()
b_newpass = newpass.encode('utf-8')
@ -41,62 +46,69 @@ def setencpasswd(vmname, newpass):
try:
clientsdb = readclientsdb()
#print(clientsdb)
path = utils.get_path(clientsdb, vmname)
path = utils.get_path(clientsdb, clientemail)
#print(path)
c_id = str(path[0])
v_id = str(path[1])
#check the returned path with forward query
query = clientsdb[c_id][v_id]['hostname']
query = clientsdb[c_id]['email']
except:
raise
if query != vmname:
ioconfig.logger.critical('clients> test query returns different vmname! check clients.json consistency!')
if query != clientemail:
ioconfig.logger.critical('clients.db> test query returns different vmname! check clients db for consistency!')
raise
else:
clientsdb[c_id][v_id]['encpasswd'] = encpasswd
ioconfig.logger.info('clients> {} (clientid: {}, vmid: {}) got its management password changed!'.format(query, c_id, v_id))
clientsdb[c_id]['encpasswd'] = encpasswd
ioconfig.logger.info('client[{}]> got its management password changed!'.format(clientemail))
writeclientsdb(clientsdb)
#TODO: change lxc container password
#TODO: Send new email to the client to notify the password change. This time sending the password in plain text is not needed.
def validate(clientemail, srvpass):
""" return vmid or false if credentials match something in clientdb. useful for authing extrnal admin panels """
def validate(clientemail, password):
""" return list of owned vmids or false if credentials match an user form the database.
useful for authing extrnal admin panels """
#1. search for the client
try:
clientsdb = readclientsdb()
path = utils.get_path(clientsdb, clientemail)
c_id = str(path[0])
#check the returned path with forward query
ioconfig.logger.info('clients> {} was found with clientid: {}'.format(clientemail, c_id))
ioconfig.logger.info('client[{}]> found. path={}'.format(clientemail, str(path)))
except:
raise
ioconfig.logger.warning('clients> {} was not found in the database!'.format(clientemail))
ioconfig.logger.warning('clients.db> {} was not found in the database!'.format(clientemail))
#log bad ips here...
return False
vmlist = clientsdb[c_id]
#2. check the password
encpass = clientsdb[c_id]['encpasswd']
b_srvpass = password.encode('utf-8')
b_encpass = encpass.encode('utf-8')
if (hmac.compare_digest(bcrypt.hashpw(b_srvpass, b_encpass), b_encpass)):
#login successful
ioconfig.logger.info('client[{}]> logged in successfully'.format(clientemail))
#TODO: Notify admin
#3. generate vmlist to return the owned ids to the client.
return clientvms(clientsdb[c_id])
else:
ioconfig.logger.warning('clients> {} ACCESS DENIED!'.format(vmid))
#cant compare password
#TODO: Log attempts and block.
return {}
def clientvms(vmlist):
""" generate vmlist """
#clear unused objects. perhaps there is a better way to do this but im kinda anxious today...
vmlist.pop('name')
vmlist.pop('email')
#try each vmid owned by this user for a password match
response = []
for vmid,data in vmlist.items():
print(vmid)
print(data)
#try to capture the encrypted password
encpass = data['encpasswd']
b_srvpass = srvpass.encode('utf-8')
b_encpass = encpass.encode('utf-8')
if (hmac.compare_digest(bcrypt.hashpw(b_srvpass, b_encpass), b_encpass)):
#login successful
ioconfig.logger.info('clients> {} was validated successfully by {}'.format(vmid, clientemail))
response = { 'vmid':vmid }
else:
ioconfig.logger.warning('clients> {} ACCESS DENIED!'.format(vmid))
#cant compare password
response = { }
#TODO: this will require major rewrite again.. or it will fail to auth 2 machines with same password. lame..
return response
@ -136,5 +148,5 @@ def writeclientsdb(clientsdb):
if __name__ == '__main__':
#setencpasswd('srv.test1.com', 'todos')
validate('daniel@deflax.net', 'todos')
setencpasswd('daniel@deflax.net', 'todos')
print(validate('daniel@deflax.net', 'todos'))

View file

@ -168,7 +168,7 @@ def query_region(region_name):
for region in all_regions:
if grid_data[region]['region'] == region_name:
logger.info('grid> region ' + region_name + ' found')
logger.info('region[{}]> found: id={}'.format(region_name, region))
return grid_data[region]['id']
break
logger.error('grid> cant find region ' + region_name)
@ -339,10 +339,10 @@ def query_vm(req_vmid):
try:
vm_type = grid_data[str(region_id)][str(slave_id)][str(target)]['type']
except:
logger.error('{}> type is unknown!'.format(vm_id))
logger.error('vm[{}]> type is unknown!'.format(vm_id))
raise
logger.info('{}> region={}, slave={}, type={} found.'.format(target, region_id, slave_id, vm_type))
logger.info('vm[{}]> type {} found. path=region={} found.'.format(target, vm_type, str(path)))
return slave_id, vm_type

View file

@ -148,13 +148,13 @@ def vmstart(vm_id):
proxobject = auth(slave_id)
vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> starting %s %s' % (slave_name, vm_type, vm_id))
ioconfig.logger.info('slave[%s]> starting %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.start.post()
if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.start.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result))
#ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'START' }
return response
@ -165,13 +165,13 @@ def vmshutdown(vm_id):
proxobject = auth(slave_id)
vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> acpi shutdown %s %s' % (slave_name, vm_type, vm_id))
ioconfig.logger.info('slave[%s]> acpi shutdown %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.stop.post()
if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.stop.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result))
#ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'SHUTDOWN', 'vmid':vm_id }
return response
@ -182,13 +182,13 @@ def vmstop(vm_id):
proxobject = auth(slave_id)
vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> power off %s %s' % (slave_name, vm_type, vm_id))
ioconfig.logger.info('slave[%s]> power off %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.stop.post()
if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.stop.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result))
#ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'STOP', 'vmid':vm_id }
return response
@ -199,13 +199,13 @@ def vmshutdown(vm_id):
proxobject = auth(slave_id)
vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> acpi shutdown sent to %s %s' % (slave_name, vm_type, vm_id))
ioconfig.logger.info('slave[%s]> acpi shutdown sent to %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.shutdown.post()
if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.shutdown.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result))
#ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'SHUTDOWN', 'vmid':vm_id }
return response
@ -216,13 +216,13 @@ def vmsuspend(vm_id):
proxobject = auth(slave_id)
vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> suspending %s %s' % (slave_name, vm_type, vm_id))
ioconfig.logger.info('slave[%s]> suspending %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.suspend.post()
if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.suspend.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result))
#ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'SUSPEND', 'vmid':vm_id }
return response
@ -233,13 +233,13 @@ def vmresume(vm_id):
proxobject = auth(slave_id)
vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> resuming %s %s' % (slave_name, vm_type, vm_id))
ioconfig.logger.info('slave[%s]> resuming %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.resume.post()
if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.resume.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result))
#ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'RESUME', 'vmid':vm_id }
return response
@ -250,7 +250,7 @@ def vmvnc(vm_id):
proxobject = auth(slave_id)
vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> invoking vnc ticket for %s %s' % (slave_name, vm_type, vm_id))
ioconfig.logger.info('slave[%s]> invoking vnc ticket for %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm':
ticket = proxobject.nodes(slave_name).qemu(vm_id).vncproxy.post(websocket=1)
@ -282,7 +282,7 @@ def vmvnc(vm_id):
external_url = ioconfig.parser.get('general', 'novnc_url')
prefix = external_url + "/?host=" + myip + "&port=" + listenport + "&encrypt=0&true_color=1&password="
vnc_url = prefix + ticket['ticket']
ioconfig.logger.info('grid[{}]> {}'.format(slave_name, vnc_url))
ioconfig.logger.info('slave[{}]> {}'.format(slave_name, vnc_url))
response = { 'status':'VNC', 'fqdn':external_url, 'host':myip, 'port':listenport, 'encrypt':'0', 'true_color':'1', 'ticket':ticket['ticket'] }
return response

View file

@ -2,8 +2,27 @@
#
# helper functions
from copy import deepcopy
import functools
from copy import deepcopy
from random import SystemRandom
def genpassword(length=20):
""" generates pseudo-random password """
choice = SystemRandom().choice
charsets = [
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789',
'%&=?+~#-_.',
]
pwd = []
charset = choice(charsets)
while len(pwd) < length:
pwd.append(choice(charset))
charset = choice(list(set(charsets) - set([charset])))
return "".join(pwd)
def dict_merge(target, *args):
""" Recursively merges mutiple dicts """
@ -24,6 +43,7 @@ def dict_merge(target, *args):
target[k] = deepcopy(v)
return target
def find_rec(search_dict, field):
"""
Takes a dict with nested lists and dicts,
@ -68,3 +88,4 @@ def chained_get(dct, *keys):
return 'NA' if level is SENTRY else level.get(key, SENTRY)
return functools.reduce(getter, keys, dct)