diff --git a/confluent/auth.py b/confluent/auth.py index 09641b1a..f60a2fc6 100644 --- a/confluent/auth.py +++ b/confluent/auth.py @@ -21,9 +21,9 @@ import confluent.config.configmanager as configmanager import eventlet -import Crypto.Protocol.KDF as kdf -import Crypto.Hash as hash -import os +import Crypto.Protocol.KDF as KDF +import hashlib +import hmac import time _passcache = {} @@ -33,7 +33,7 @@ _passchecking = {} def _prune_passcache(): # This function makes sure we don't remember a passphrase in memory more # than 10 seconds - while (1): + while True: curtime = time.time() for passent in _passcache.iterkeys(): if passent[2] < curtime - 10: @@ -88,7 +88,7 @@ def authorize(name, element, tenant=False, operation='create'): manager = configmanager.ConfigManager(tenant) userobj = manager.get_user(user) if userobj: # returning - return (userobj, manager, user, tenant) + return userobj, manager, user, tenant return None @@ -110,7 +110,7 @@ def check_user_passphrase(name, passphrase, element=None, tenant=False): embedded in name) """ # The reason why tenant is 'False' instead of 'None': - # None means explictly not a tenant. False means check + # None means explicitly not a tenant. False means check # the username for signs of being a tenant # If there is any sign of guessing on a user, all valid and # invalid attempts are equally slowed to no more than 20 per second @@ -135,7 +135,7 @@ def check_user_passphrase(name, passphrase, element=None, tenant=False): cfm = configmanager.ConfigManager(tenant) ucfg = cfm.get_user(user) if ucfg is None or 'cryptpass' not in ucfg: - eventlet.sleep(0.05) # stall even on test for existance of a username + eventlet.sleep(0.05) # stall even on test for existence of a username return None _passchecking[(user, tenant)] = True # TODO(jbjohnso): WORKERPOOL @@ -143,8 +143,8 @@ def check_user_passphrase(name, passphrase, element=None, tenant=False): # throw it at the worker pool when implemented # maybe a distinct worker pool, wondering about starving out non-auth stuff salt, crypt = ucfg['cryptpass'] - crypted = kdf.PBKDF2(passphrase, salt, 32, 10000, - lambda p, s: hash.HMAC.new(p, s, hash.SHA256).digest() + crypted = KDF.PBKDF2(passphrase, salt, 32, 10000, + lambda p, s: hmac.new(p, s, hashlib.sha256).digest() ) del _passchecking[(user, tenant)] eventlet.sleep(0.05) # either way, we want to stall so that client can't diff --git a/confluent/exceptions.py b/confluent/exceptions.py index f18c9fcc..e8a8108d 100644 --- a/confluent/exceptions.py +++ b/confluent/exceptions.py @@ -13,24 +13,30 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + + class ConfluentException(Exception): pass + class NotFoundException(ConfluentException): # Something that could be construed as a name was not found # basically, picture an http error code 404 pass + class InvalidArgumentException(ConfluentException): # Something from the remote client wasn't correct # like http code 400 pass + class TargetEndpointUnreachable(ConfluentException): # A target system was unavailable. For example, a BMC # was unreachable. http code 504 pass + class ForbiddenRequest(ConfluentException): # The client request is not allowed by authorization engine pass diff --git a/confluent/httpapi.py b/confluent/httpapi.py index 4201b239..c0936122 100644 --- a/confluent/httpapi.py +++ b/confluent/httpapi.py @@ -52,7 +52,7 @@ def group_creation_resources(): yield confluent.messages.Attributes( kv={'name': None}, desc="Name of the group").html() + '
' yield confluent.messages.ListAttributes(kv={'nodes': []}, - desc='Nodes to add to the group').html() + '
\n' + desc='Nodes to add to the group').html() + '
\n' for attr in sorted(attribs.node.iterkeys()): if attr == 'groups': continue @@ -60,7 +60,8 @@ def group_creation_resources(): yield confluent.messages.CryptedAttributes( kv={attr: None}, desc=attribs.node[attr]['description']).html() + '
\n' - elif 'type' in attribs.node[attr] and list == attribs.node[attr]['type']: + elif 'type' in attribs.node[attr] and list == attribs.node[attr][ + 'type']: yield confluent.messages.ListAttributes( kv={attr: []}, desc=attribs.node[attr]['description']).html() + '
\n' @@ -78,7 +79,8 @@ def node_creation_resources(): yield confluent.messages.CryptedAttributes( kv={attr: None}, desc=attribs.node[attr]['description']).html() + '
\n' - elif 'type' in attribs.node[attr] and list == attribs.node[attr]['type']: + elif 'type' in attribs.node[attr] and list == attribs.node[attr][ + 'type']: yield confluent.messages.ListAttributes( kv={attr: []}, desc=attribs.node[attr]['description']).html() + '
\n' @@ -87,6 +89,7 @@ def node_creation_resources(): kv={attr: None}, desc=attribs.node[attr]['description']).html() + '
\n' + create_resource_functions = { '/nodes/': node_creation_resources, '/groups/': group_creation_resources, @@ -127,7 +130,7 @@ def _get_query_dict(env, reqbody, reqtype): elif 'application/json' == reqtype: pbody = json.loads(reqbody) for key in pbody.iterkeys(): - qdict[key] = pbody[ky] + qdict[key] = pbody[key] if 'restexplorerhonorkey' in qdict: nqdict = {} for key in qdict: @@ -145,6 +148,7 @@ def _authorize_request(env, operation): """ authdata = False + name = '' cookie = Cookie.SimpleCookie() if 'HTTP_COOKIE' in env: #attempt to use the cookie. If it matches @@ -178,10 +182,10 @@ def _authorize_request(env, operation): 'target': env['PATH_INFO'], } authinfo = {'code': 200, - 'cookie': cookie, - 'cfgmgr': authdata[1], - 'username': authdata[2], - 'userdata': authdata[0]} + 'cookie': cookie, + 'cfgmgr': authdata[1], + 'username': authdata[2], + 'userdata': authdata[0]} if authdata[3] is not None: auditmsg['tenant'] = authdata[3] authinfo['tenant'] = authdata[3] @@ -191,13 +195,13 @@ def _authorize_request(env, operation): return authinfo else: return {'code': 401} - # TODO(jbjohnso): actually evaluate the request for authorization - # In theory, the x509 or http auth stuff will get translated and then - # passed on to the core authorization function in an appropriate form - # expresses return in the form of http code - # 401 if there is no known identity - # 403 if valid identity, but no access - # going to run 200 just to get going for now + # TODO(jbjohnso): actually evaluate the request for authorization + # In theory, the x509 or http auth stuff will get translated and then + # passed on to the core authorization function in an appropriate form + # expresses return in the form of http code + # 401 if there is no known identity + # 403 if valid identity, but no access + # going to run 200 just to get going for now def _pick_mimetype(env): @@ -239,6 +243,7 @@ def resourcehandler(env, start_response): yield '500 - Internal Server Error' return + def resourcehandler_backend(env, start_response): """Function to handle new wsgi requests """ @@ -327,10 +332,11 @@ def resourcehandler_backend(env, start_response): try: rsp = json.dumps(rspdata) except UnicodeDecodeError: - rsp = json.dumps(rspdata, encoding='cp437') - except UnicodeDecodeError: - rsp = json.dumps({'session': querydict['session'], - 'data': 'DECODEERROR'}) + try: + rsp = json.dumps(rspdata, encoding='cp437') + except UnicodeDecodeError: + rsp = json.dumps({'session': querydict['session'], + 'data': 'DECODEERROR'}) start_response('200 OK', headers) yield rsp return @@ -367,7 +373,7 @@ def resourcehandler_backend(env, start_response): def _assemble_html(responses, resource, querydict, url): yield '' \ 'Confluent REST Explorer: ' + url + '' \ - '
' + '' if querydict: yield 'Response to input data:
' + \ json.dumps(querydict, separators=(',', ': '), @@ -399,7 +405,8 @@ def _assemble_html(responses, resource, querydict, url): firstpass = True for y in create_resource_functions[url](): if firstpass: - yield "
Define new resource in %s:
" % url.split("/")[-2] + yield "
Define new resource in %s:
" % \ + url.split("/")[-2] firstpass = False yield y yield ('' @@ -464,7 +471,7 @@ def serve(): #also, the potential for direct http can be handy #todo remains unix domain socket for even http eventlet.wsgi.server(eventlet.listen(("", 4005)), resourcehandler, - log=False, log_output=False, debug=False) + log=False, log_output=False, debug=False) class HttpApi(object): @@ -475,4 +482,5 @@ class HttpApi(object): auditlog = log.Logger('audit') self.server = eventlet.spawn(serve) + _cleaner = eventlet.spawn(_sessioncleaner) diff --git a/confluent/log.py b/confluent/log.py index 7447e0a6..064bff77 100644 --- a/confluent/log.py +++ b/confluent/log.py @@ -80,6 +80,7 @@ import time _loggers = {} + class Events(object): ( undefined, clearscreen, clientconnect, clientdisconnect, @@ -186,7 +187,7 @@ class Logger(object): textfile = open(self.textpath, mode='r') binfile = open(self.binpath, mode='r') except IOError: - return ('', 0) + return '', 0 fcntl.flock(binfile, fcntl.LOCK_SH) binfile.seek(0, 2) binidx = binfile.tell() - 16 @@ -210,14 +211,14 @@ class Logger(object): textdata = '' fcntl.flock(textfile, fcntl.LOCK_SH) while offsets: - (offset, len) = offsets.pop() + (offset, length) = offsets.pop() textfile.seek(offset, 0) - textdata += textfile.read(len) + textdata += textfile.read(length) fcntl.flock(textfile, fcntl.LOCK_UN) textfile.close() if termstate is None: termstate = 0 - return (textdata, termstate) + return textdata, termstate def log(self, logdata=None, ltype=None, event=0, eventdata=None): if type(logdata) not in (str, unicode, dict): diff --git a/confluent/main.py b/confluent/main.py index 81d63f15..2e5a468c 100644 --- a/confluent/main.py +++ b/confluent/main.py @@ -29,15 +29,14 @@ import confluent.pluginapi as pluginapi import confluent.httpapi as httpapi import confluent.sockapi as sockapi import eventlet -import eventlet.backdoor as backdoor -from eventlet.green import socket -from eventlet import wsgi +#import eventlet.backdoor as backdoor import fcntl -import multiprocessing +#import multiprocessing import sys import os import signal + def _daemonize(): thispid = os.fork() if thispid > 0: @@ -48,7 +47,7 @@ def _daemonize(): if thispid > 0: print 'confluent server starting as pid %d' % thispid os._exit(0) - os.closerange(0,2) + os.closerange(0, 2) os.umask(63) os.open(os.devnull, os.O_RDWR) os.dup2(0, 1) @@ -69,7 +68,8 @@ def _checkpidfile(): fcntl.flock(pidfile, fcntl.LOCK_EX) pid = pidfile.read() if pid != '': - print '/var/run/confluent/pid exists and indicates %s is still running' % pid + print ('/var/run/confluent/pid exists and indicates %s is still ' + 'running' % pid) sys.exit(1) pidfile.write(str(os.getpid())) fcntl.flock(pidfile, fcntl.LOCK_UN) @@ -84,21 +84,24 @@ def _checkpidfile(): fcntl.flock(pidfile, fcntl.LOCK_SH) pid = pidfile.read() if pid != str(os.getpid()): - print '/var/run/confluent/pid exists and indicates %s is still running' % pid + print ('/var/run/confluent/pid exists and indicates %s is still ' + 'running' % pid) sys.exit(1) fcntl.flock(pidfile, fcntl.LOCK_UN) pidfile.close() -def terminate(signal, frame): +def terminate(signalname, frame): sys.exit(0) -def exit(): + +def doexit(): pidfile = open('/var/run/confluent/pid') pid = pidfile.read() if pid == str(os.getpid()): os.remove('/var/run/confluent/pid') + def run(): _checkpidfile() pluginapi.load_plugins() @@ -106,7 +109,7 @@ def run(): _updatepidfile() signal.signal(signal.SIGINT, terminate) signal.signal(signal.SIGTERM, terminate) - #TODO(jbjohnso): eventlet has a bug about unix domain sockets, this code + #TODO(jbjohnso): eventlet has a bug about unix domain sockets, this code #works with bugs fixed #dbgsock = eventlet.listen("/var/run/confluent/dbg.sock", # family=socket.AF_UNIX) @@ -115,7 +118,6 @@ def run(): webservice.start() sockservice = sockapi.SockApi() sockservice.start() - atexit.register(exit) - while (1): + atexit.register(doexit) + while 1: eventlet.sleep(100) - diff --git a/confluent/messages.py b/confluent/messages.py index c6eaa25d..94c5f2fd 100644 --- a/confluent/messages.py +++ b/confluent/messages.py @@ -39,7 +39,6 @@ def _htmlify_structure(indict): return ret + '' - class ConfluentMessage(object): readonly = False defaultvalue = '' @@ -68,23 +67,26 @@ class ConfluentMessage(object): for key in self.kvpairs.iterkeys(): val = self.kvpairs[key] value = self.defaultvalue - type = self.defaulttype + valtype = self.defaulttype notes = [] if val is not None and 'value' in val: value = val['value'] if 'inheritedfrom' in val: notes.append('Inherited from %s' % val['inheritedfrom']) if 'expression' in val: - notes.append('Derived from expression "%s"' % val['expression']) + notes.append( + 'Derived from expression "%s"' % val['expression']) elif val is not None and 'expression' in val and 'broken' in val: value = "*BROKEN*" - notes.append('Derived from expression "%s"' % val['expression']) + notes.append( + 'Derived from expression "%s"' % val['expression']) notes.append('Broken because of %s' % val['broken']) elif val is not None and 'expression' in val: value = val['expression'] if value is None: value = '' - if val is not None and value == '' and 'isset' in val and val['isset'] is True: + if val is not None and value == '' and 'isset' in val and val[ + 'isset'] is True: # an encrypted value, put some *** to show it is set # in the explorer if 'inheritedfrom' in val: @@ -95,19 +97,19 @@ class ConfluentMessage(object): if len(val) == 0 and not self.readonly: snippet += ('' - ).format(type, key, self.desc) + ).format(valtype, key, self.desc) for v in val: if self.readonly: snippet += _htmlify_structure(v) else: snippet += ('' - ).format(type, key, v, self.desc) + ).format(valtype, key, v, self.desc) if not self.readonly: snippet += ( '' '').format(type, key, self.desc) + 'value="{1}">').format(valtype, key, self.desc) return snippet if self.readonly: snippet += "{0}: {1}".format(key, value) @@ -116,7 +118,7 @@ class ConfluentMessage(object): '' - ).format(type, key, value, self.desc) + ).format(valtype, key, value, self.desc) if len(notes) > 0: snippet += '(' + ','.join(notes) + ')' return snippet @@ -128,7 +130,6 @@ class DeletedResource(ConfluentMessage): class ConfluentChoiceMessage(ConfluentMessage): - def html(self): snippet = "" for key in self.kvpairs.iterkeys(): @@ -143,7 +144,7 @@ class ConfluentChoiceMessage(ConfluentMessage): snippet += '' % (opt, opt) snippet += '' snippet += '