def bunchify(data, name=None): if isinstance(data, Bunch): return data if isinstance(data, list): if name == 'rowset': # we unpack these into a dictionary based on name return Bunch({i['name']: bunchify(i, 'rowset') for i in data}) return [bunchify(i) for i in data] if isinstance(data, dict): data = data.copy() if name == 'row' and 'row' in data: # Special case of CCP's silly key:value text. pass if name and name in data and not data.get(name, '').strip(): data.pop(name) if name == 'rowset' and 'name' in data: data.pop('name') if len(data) == 1 and isinstance(data.values()[0], dict): return bunchify(data.values()[0], data.keys()[0]) result = Bunch({ k: bunchify( [v] if k in ('row', ) and not isinstance(v, list) else v, k ) for k, v in data.iteritems() if k != 'rowset' }) if 'rowset' in data: rowset = bunchify(data['rowset'] if isinstance(data['rowset'], list) else [data['rowset']], 'rowset') result.update(rowset) if name == 'rowset': # rowsets always contain rows, even if there are no results result.setdefault('row', []) return result if isinstance(data, str): data = data.decode('utf-8') if isinstance(data, (str, unicode)): try: return number(data) except ValueError: pass try: return boolean(data) except ValueError: pass if ',' in data and (name in ('key', 'columns') or ' ' not in data): return array(data) return data
def __init__(self, *args, **kw): super(RootController, self).__init__(*args, **kw) self.authorize = AuthorizeHandler() # to avoid gumming up the @authorize decorator if boolean(config.get('debug', False)): self.dev = DeveloperTools()
def __init__(self, config): if not 'host' in config: raise MailConfigurationException('No server configured for SMTP') self.host = native(config.get('host')) self.tls = config.get('tls', 'optional') self.certfile = config.get('certfile', None) self.keyfile = config.get('keyfile', None) self.port = int(config.get('port', 465 if self.tls == 'ssl' else 25)) self.local_hostname = native(config.get('local_hostname', '')) or None self.username = native(config.get('username', '')) or None self.password = native(config.get('password', '')) or None self.timeout = config.get('timeout', None) if self.timeout: self.timeout = int(self.timeout) self.debug = boolean(config.get('debug', False)) self.pipeline = config.get('pipeline', None) if self.pipeline is not None: self.pipeline = int(self.pipeline) self.connection = None self.sent = 0
def __init__(self, *args, **kw): super(RootController, self).__init__(*args, **kw) self.authorize = AuthorizeHandler( ) # to avoid gumming up the @authorize decorator if boolean(config.get('debug', False)): self.dev = DeveloperTools()
def embed(self, asset, preload=True, autoplay=False, loop=False, controls=True, width=None): path = asset.path + '/view:download/' + asset.filename + "?inline=True" preload = boolean(preload) autoplay = boolean(autoplay) loop = boolean(loop) controls = boolean(controls) return tag.audio ( preload = preload, autobuffer = preload, autoplay = autoplay, loop = loop, controls = controls, width = width, src = path, type=asset.mimetype )
def profiling(app, config): if not defaultbool(config.get('web.profile', False), ['repoze']): return app log.debug("Enabling profiling support.") try: from repoze.profile.profiler import AccumulatingProfileMiddleware return AccumulatingProfileMiddleware( app, log_filename=config.get('web.profile.log', 'profile.prof'), discard_first_request=boolean(config.get('web.profile.discard', 'true')), flush_at_shutdown=boolean(config.get('web.profile.flush', 'true')), path=config.get('web.profile.path', '/__profile__') ) except ImportError: # pragma: no cover raise ImportError("You must install repoze.profile to enable profiling")
def setup(self): self.model.__dict__['engine'] = engine_from_config(self.config, prefix="%s.sqlalchemy." % (self.prefix,)) self.model.metadata.bind = self.model.engine args = dict( bind=self.model.engine, autocommit=boolean(self.config.get('%s.autocommit' % (self.prefix,), False)), autoflush=boolean(self.config.get('%s.autoflush' % (self.prefix,), True)), twophase=boolean(self.config.get('%s.twophase' % (self.prefix,), False)), ) setup = getattr(self.model, 'setup', None) if hasattr(setup, '__call__'): warnings.warn("Use of the hard-coded 'setup' callback is deprecated.\n" "Use the 'ready' callback instead.", DeprecationWarning) args = setup(args) self._session = sessionmaker(**args)
def refresh(self): try: self.key.pull() except: log.exception("Unable to reload key.") if boolean(config.get("debug", False)): raise return "json:", dict(success=False) return "json:", dict(success=True)
def get(self, admin=False): admin = boolean(admin) if admin and not is_administrator: raise HTTPNotFound() credentials = user.credentials if admin: # Don't send the verification code for the API keys. credentials = EVECredential.objects.only("violation", "key", "verified", "owner") return "brave.core.key.template.list", dict(area="keys", admin=admin, records=credentials)
def refresh(self): try: self.key.pull() except: log.exception("Unable to reload key.") if boolean(config.get('debug', False)): raise return 'json:', dict(success=False) return 'json:', dict(success=True)
def authenticate(identifier, password): """Given an e-mail address (or Yubikey OTP) and password, authenticate a user.""" ts = time() # Record the query = dict(active=True) # Gracefully handle extended characters in passwords. # The password storage algorithm works in binary. if isinstance(password, unicode): password = password.encode('utf8') # Build the MongoEngine query to find if '@' in identifier: query[b'email'] = identifier elif len(identifier) == 44: query[b'otp'] = identifier[:12] else: query[b'username'] = identifier user = User.objects(**query).first() if not user or not User.password.check(user.password, password) or (user.rotp and len(user.otp) != 0 and not 'otp' in query): if user: LoginHistory(user, False, request.remote_addr).save() # Prevent basic timing attacks; always take at least one second to process. sleep(max(min(1 - (time() - ts), 0), 1)) return None # Validate Yubikey OTP if 'otp' in query: client = yubico.Yubico( config['yubico.client'], config['yubico.key'], boolean(config.get('yubico.secure', False)) ) try: status = client.verify(identifier, return_response=True) except: return None if not status: return None user.update(set__seen=datetime.utcnow()) # Record the fact the user signed in. LoginHistory(user, True, request.remote_addr).save() return user.id, user
def get(self, admin=False): admin = boolean(admin) if admin and not is_administrator: raise HTTPNotFound() credentials = user.credentials if admin: #Don't send the verification code for the API keys. credentials = EVECredential.objects.only('violation', 'key', 'verified', 'owner') return 'brave.core.key.template.list', dict(area='keys', admin=admin, records=credentials)
def get(self, admin=False): admin = boolean(admin) if admin and not is_administrator: raise HTTPNotFound() credentials = user.credentials if admin: #Don't send the verification code for the API keys. credentials = EVECredential.objects.only('violation', 'key', 'verified', 'owner') return 'brave.core.key.template.list', dict( area = 'keys', admin = admin, records = credentials )
def __default__(self, group, endpoint, token=None, anonymous=None, **kw): from brave.core.application.model import ApplicationGrant anonymous = None if anonymous is None else boolean(anonymous) log.info("service={0!r} group={1} endpoint={2} token={3} anonymous={4} data={5}".format( request.service, group, endpoint, token, anonymous, kw )) # Prevent access to certain overly-broad API calls. if group == 'account' and endpoint != 'AccountStatus': return dict(success=False, reason='endpoint.restricted', message="Access restricted to endpoint: {0}.{1}".format(group, endpoint)) try: # Get the appropriate grant. token = ApplicationGrant.objects.get(id=token, application=request.service) if token else None except ApplicationGrant.DoesNotExist: return dict(success=False, reason='grant.invalid', message="Application grant invalid or expired.") try: # Load the API endpoint. call = getattr(getattr(api, group, None), endpoint) except AttributeError: return dict(success=False, reason='endpoint.invalid', message="Unknown API endpoint: {0}.{1}".format(group, endpoint)) key = None if anonymous is False or token or call.mask: # Check that this grant allows calls to this API endpoint. if call.mask and (not token or not token.mask.has_access(call.mask)): return dict(success=False, reason='grant.unauthorized', message="Not authorized to call endpoint: {0}.{1}".format(group, endpoint)) # Find an appropriate key to use for this request if one is required or anonymous=False. key = token.character.credential_for(call.mask) if not key: return dict(success=False, reason='key.notfound', message="Could not find EVE API key that authorizes endpoint: {0}.{1}".format(group, endpoint)) try: # Perform the query or get the cached result. result = call(key, **kw) if key else call(**kw) except Exception as e: log.exception("Unable to process request.") return dict(success=False, reason='eve.unknown', message="Encountered unexpected error during EVE API call.", detail=unicode(e)) # Ensure the presence of a 'success' key for quick validation of calls and return the result. result.update(success=True) return result
def __init__(self, application, config=dict(), prefix='auth.'): self.application = application prefix_length = len(prefix) our_config = Bunch(default_config.copy()) for i, j in config.iteritems(): if i.startswith(prefix): our_config[i[prefix_length:]] = j our_config.intercept = [i.strip() for i in array(our_config.intercept)] our_config.internal = boolean(our_config.internal) if our_config.lookup is None: raise Exception('You must define an authentication lookup method.') our_config.lookup = self.get_method(our_config.lookup) our_config.authenticate = self.get_method(our_config.authenticate) web.auth.config = our_config
def __init__(self, config): self.host = native(config.get("host", "127.0.0.1")) self.tls = config.get("tls", "optional") self.certfile = config.get("certfile", None) self.keyfile = config.get("keyfile", None) self.port = int(config.get("port", 465 if self.tls == "ssl" else 25)) self.local_hostname = native(config.get("local_hostname", "")) or None self.username = native(config.get("username", "")) or None self.password = native(config.get("password", "")) or None self.timeout = config.get("timeout", None) if self.timeout: self.timeout = int(self.timeout) self.debug = boolean(config.get("debug", False)) self.pipeline = config.get("pipeline", None) if self.pipeline not in (None, True, False): self.pipeline = int(self.pipeline) self.connection = None self.sent = 0
def __init__(self, config): self.host = native(config.get('host', '127.0.0.1')) self.tls = config.get('tls', 'optional') self.certfile = config.get('certfile', None) self.keyfile = config.get('keyfile', None) self.port = int(config.get('port', 465 if self.tls == 'ssl' else 25)) self.local_hostname = native(config.get('local_hostname', '')) or None self.username = native(config.get('username', '')) or None self.password = native(config.get('password', '')) or None self.timeout = config.get('timeout', None) if self.timeout: self.timeout = int(self.timeout) self.debug = boolean(config.get('debug', False)) self.pipeline = config.get('pipeline', None) if self.pipeline not in (None, True, False): self.pipeline = int(self.pipeline) self.connection = None self.sent = 0
def debugging(app, config): if not defaultbool(config.get('web.debug', True), ['weberror']): return app try: localconfig = dict(debug=config.get('debug', False)) for i, j in config.iteritems(): if i.startswith('debug.'): localconfig[i[6:]] = j if boolean(config.get('debug', False)): log.debug("Debugging enabled; exceptions raised will display an interactive traceback.") from weberror.evalexception import EvalException return EvalException(app, config, **localconfig) else: log.debug("Debugging disabled; exceptions raised will display a 500 error.") from weberror.errormiddleware import ErrorMiddleware return ErrorMiddleware(app, config, **localconfig) except ImportError: # pragma: no cover raise ImportError("You must install WebError to enable debugging")
def static(app, config): # Enabled explicitly or while debugging so you can use Paste's HTTP server. if config.get('web.static', None) is None and not boolean(config.get('debug', False)): return app if not boolean(config.get('web.static', False)): return app from paste.cascade import Cascade from paste.fileapp import DirectoryApp path = config.get('web.static.path', None) base = config.get('web.static.root', None) if path is None: # Attempt to discover the path automatically. module = __import__(config['web.root'].__module__) parts = config['web.root'].__module__.split('.')[1:] path = module.__file__ if not parts: parts = ['.'] while parts: # pragma: no cover # Search up the package tree, in case this is an application in a sub-module. path = os.path.abspath(path) path = os.path.dirname(path) path = os.path.join(path, 'public') log.debug("Trying %r", path) if os.path.isdir(path): break if parts[0] == '.': break module = getattr(module, parts.pop(0)) path = module.__file__ if not os.path.isdir(path): log.warn("Unable to find folder to serve static content from. " "Please specify web.static.path in your config.") return app log.debug("Serving static files from '%s' at '%s'.", path, base or '/') subapp = DirectoryApp(path) if base: def inner(app, environ, start_response): from webob.exc import HTTPNotFound if not environ['PATH_INFO'].startswith(base): return HTTPNotFound()(environ, start_response) environ['PATH_INFO'] = environ['PATH_INFO'][len(base.rstrip('/')):] return app(environ, start_response) subapp = functools.partial(inner, subapp) return Cascade([subapp, app], catch=[401, 403, 404])
def post(self, **post): try: data = Bunch(post) except Exception as e: if config.get('debug', False): raise return 'json:', dict(success=False, message=_("Unable to parse data."), data=post, exc=str(e)) query = dict(active=True) query[b'username'] = data.id user = User.objects(**query).first() if data.form == "changepassword": passwd_ok, error_msg = _check_password(data.passwd, data.passwd1) if not passwd_ok: return 'json:', dict(success=False, message=error_msg, data=data) if isinstance(data.old, unicode): data.old = data.old.encode('utf-8') if not User.password.check(user.password, data.old): return 'json:', dict(success=False, message=_("Old password incorrect."), data=data) #If the password isn't strong enough, reject it if (zxcvbn.password_strength(data.passwd).get("score") < MINIMUM_PASSWORD_STRENGTH): return 'json:', dict( success=False, message= _("Password provided is too weak. please add more characters, or include lowercase, uppercase, and special characters." ), data=data) user.password = data.passwd user.save() elif data.form == "addotp": if isinstance(data.password, unicode): data.password = data.password.encode('utf-8') identifier = data.otp client = yubico.Yubico(config['yubico.client'], config['yubico.key'], boolean(config.get('yubico.secure', False))) if not User.password.check(user.password, data.password): return 'json:', dict(success=False, message=_("Password incorrect."), data=data) try: status = client.verify(identifier, return_response=True) except: return 'json:', dict(success=False, message=_("Failed to contact YubiCloud."), data=data) if not status: return 'json:', dict(success=False, message=_("Failed to verify key."), data=data) if not User.addOTP(user, identifier[:12]): return 'json:', dict(success=False, message=_("YubiKey already exists."), data=data) elif data.form == "removeotp": identifier = data.otp if not User.removeOTP(user, identifier[:12]): return 'json:', dict(success=False, message=_("YubiKey invalid."), data=data) elif data.form == "configureotp": if isinstance(data.password, unicode): data.password = data.password.encode('utf-8') rotp = True if 'rotp' in data else False if not User.password.check(user.password, data.password): return 'json:', dict(success=False, message=_("Password incorrect."), data=data) user.rotp = rotp user.save() #Handle the user attempting to delete their account elif data.form == "deleteaccount": if isinstance(data.passwd, unicode): data.passwd = data.passwd.encode('utf-8') #Make the user enter their username so they know what they're doing. if not user.username == data.username.lower(): return 'json:', dict(success=False, message=_("Username incorrect."), data=data) #Check whether the user's supplied password is correct if not User.password.check(user.password, data.passwd): return 'json:', dict(success=False, message=_("Password incorrect."), data=data) #Make them type "delete" exactly if not data.confirm == "delete": return 'json:', dict( success=False, message=_( "Delete was either misspelled or not lowercase."), data=data) #Delete the user account and then deauthenticate the browser session log.info("User %s authorized the deletion of their account.", user) user.delete() deauthenticate() #Redirect user to the root of the server instead of the settings page return 'json:', dict(success=True, location="/") #Handle the user attempting to change the email address associated with their account elif data.form == "changeemail": if isinstance(data.passwd, unicode): data.passwd = data.passwd.encode('utf-8') #Check whether the user's supplied password is correct if not User.password.check(user.password, data.passwd): return 'json:', dict(success=False, message=_("Password incorrect."), data=data) #Check that the two provided email addresses match if not data.newEmail.lower() == data.newEmailConfirm.lower(): return 'json:', dict( success=False, message=_("Provided email addresses do not match."), data=data) #Make sure that the provided email address is a valid form for an email address v = EmailValidator() email = data.newEmail email, err = v.validate(email) if err: return 'json:', dict( success=False, message=_("Invalid email address provided."), data=data) #Make sure that the new email address is not already taken count = User.objects.filter(**{ "email": data.newEmail.lower() }).count() if not count == 0: return 'json:', dict( success=False, message=_("The email address provided is already taken."), data=data) #Change the email address in the database and catch any email validation exceptions that mongo throws user.email = data.newEmail.lower() try: user.save() except ValidationError: return 'json:', dict( success=False, message=_("Invalid email address provided."), data=data) except NotUniqueError: return 'json:', dict( success=False, message=_("The email address provided is already taken."), data=data) #Handle the user attempting to merge 2 accounts together elif data.form == "mergeaccount": if isinstance(data.passwd, unicode): data.passwd = data.passwd.encode('utf-8') if isinstance(data.passwd2, unicode): data.passwd2 = data.passwd2.encode('utf-8') #Make the user enter their username so they know what they're doing. if user.username != data.username.lower( ) and user.username != data.username: return 'json:', dict(success=False, message=_("First username incorrect."), data=data) #Check whether the user's supplied password is correct if not User.password.check(user.password, data.passwd): return 'json:', dict(success=False, message=_("First password incorrect."), data=data) #Make sure the user isn't trying to merge their account into itself. if data.username.lower() == data.username2.lower(): return 'json:', dict( success=False, message=_("You can't merge an account into itself."), data=data) #Make the user enter the second username so we can get the User object they want merged in. if not User.objects( username=data.username2.lower()) and not User.objects( username=data.username2): return 'json:', dict( success=False, message=_("Unable to find user by second username."), data=data) other = User.objects(username=data.username2).first() if not other: other = User.objects(username=data.username2.lower()).first() #Check whether the user's supplied password is correct if not User.password.check(other.password, data.passwd2): return 'json:', dict(success=False, message=_("Second password incorrect."), data=data) #Make them type "merge" exactly if data.confirm != "merge": return 'json:', dict( success=False, message=_("Merge was either misspelled or not lowercase."), data=data) log.info("User %s merged account %s into %s.", user.username, other.username, user.username) user.merge(other) #Redirect user to the root of the server instead of the settings page return 'json:', dict(success=True, location="/") else: return 'json:', dict(success=False, message=_("Form does not exist."), location="/") return 'json:', dict(success=True, location="/account/settings")
def post(self, **post): try: data = Bunch(post) except Exception as e: if config.get('debug', False): raise return 'json:', dict(success=False, message=_("Unable to parse data."), data=post, exc=str(e)) query = dict(active=True) query[b'username'] = data.id query_user = User.objects(**query).first() if query_user.id != user.id: raise HTTPForbidden if data.form == "changepassword": passwd_ok, error_msg = _check_password(data.passwd, data.passwd1) if not passwd_ok: return 'json:', dict(success=False, message=error_msg, data=data) if isinstance(data.old, unicode): data.old = data.old.encode('utf-8') if not User.password.check(user.password, data.old): return 'json:', dict(success=False, message=_("Old password incorrect."), data=data) #If the password isn't strong enough, reject it if(zxcvbn.password_strength(data.passwd).get("score") < MINIMUM_PASSWORD_STRENGTH): return 'json:', dict(success=False, message=_("Password provided is too weak. please add more characters, or include lowercase, uppercase, and special characters."), data=data) user.password = data.passwd user.save() elif data.form == "addotp": if isinstance(data.password, unicode): data.password = data.password.encode('utf-8') identifier = data.otp client = yubico.Yubico( config['yubico.client'], config['yubico.key'], boolean(config.get('yubico.secure', False)) ) if not User.password.check(user.password, data.password): return 'json:', dict(success=False, message=_("Password incorrect."), data=data) try: status = client.verify(identifier, return_response=True) except: return 'json:', dict(success=False, message=_("Failed to contact YubiCloud."), data=data) if not status: return 'json:', dict(success=False, message=_("Failed to verify key."), data=data) if not User.addOTP(user, identifier[:12]): return 'json:', dict(success=False, message=_("YubiKey already exists."), data=data) elif data.form == "removeotp": identifier = data.otp if not User.removeOTP(user, identifier[:12]): return 'json:', dict(success=False, message=_("YubiKey invalid."), data=data) elif data.form == "configureotp": if isinstance(data.password, unicode): data.password = data.password.encode('utf-8') rotp = True if 'rotp' in data else False if not User.password.check(user.password, data.password): return 'json:', dict(success=False, message=_("Password incorrect."), data=data) user.rotp = rotp user.save() #Handle the user attempting to delete their account elif data.form == "deleteaccount": if isinstance(data.passwd, unicode): data.passwd = data.passwd.encode('utf-8') #Make the user enter their username so they know what they're doing. if not user.username == data.username.lower(): return 'json:', dict(success=False, message=_("Username incorrect."), data=data) #Check whether the user's supplied password is correct if not User.password.check(user.password, data.passwd): return 'json:', dict(success=False, message=_("Password incorrect."), data=data) #Make them type "delete" exactly if not data.confirm == "delete": return 'json:', dict(success=False, message=_("Delete was either misspelled or not lowercase."), data=data) #Delete the user account and then deauthenticate the browser session log.info("User %s authorized the deletion of their account.", user) user.delete() deauthenticate() #Redirect user to the root of the server instead of the settings page return 'json:', dict(success=True, location="/") #Handle the user attempting to change the email address associated with their account elif data.form == "changeemail": if isinstance(data.passwd, unicode): data.passwd = data.passwd.encode('utf-8') #Check whether the user's supplied password is correct if not User.password.check(user.password, data.passwd): return 'json:', dict(success=False, message=_("Password incorrect."), data=data) #Check that the two provided email addresses match if not data.newEmail.lower() == data.newEmailConfirm.lower(): return 'json:', dict(success=False, message=_("Provided email addresses do not match."), data=data) #Make sure that the provided email address is a valid form for an email address v = EmailValidator() email = data.newEmail email, err = v.validate(email) if err: return 'json:', dict(success=False, message=_("Invalid email address provided."), data=data) #Make sure that the new email address is not already taken count = User.objects.filter(**{"email": data.newEmail.lower()}).count() if not count == 0: return 'json:', dict(success=False, message=_("The email address provided is already taken."), data=data) #Change the email address in the database and catch any email validation exceptions that mongo throws user.email = data.newEmail.lower() try: user.save() except ValidationError: return 'json:', dict(success=False, message=_("Invalid email address provided."), data=data) except NotUniqueError: return 'json:', dict(success=False, message=_("The email address provided is already taken."), data=data) #Handle the user attempting to merge 2 accounts together elif data.form == "mergeaccount": if isinstance(data.passwd, unicode): data.passwd = data.passwd.encode('utf-8') if isinstance(data.passwd2, unicode): data.passwd2 = data.passwd2.encode('utf-8') #Make the user enter their username so they know what they're doing. if user.username != data.username.lower() and user.username != data.username: return 'json:', dict(success=False, message=_("First username incorrect."), data=data) #Check whether the user's supplied password is correct if not User.password.check(user.password, data.passwd): return 'json:', dict(success=False, message=_("First password incorrect."), data=data) #Make sure the user isn't trying to merge their account into itself. if data.username.lower() == data.username2.lower(): return 'json:', dict(success=False, message=_("You can't merge an account into itself."), data=data) #Make the user enter the second username so we can get the User object they want merged in. if not User.objects(username=data.username2.lower()) and not User.objects(username=data.username2): return 'json:', dict(success=False, message=_("Unable to find user by second username."), data=data) other = User.objects(username=data.username2).first() if not other: other = User.objects(username=data.username2.lower()).first() #Check whether the user's supplied password is correct if not User.password.check(other.password, data.passwd2): return 'json:', dict(success=False, message=_("Second password incorrect."), data=data) #Make them type "merge" exactly if data.confirm != "merge": return 'json:', dict(success=False, message=_("Merge was either misspelled or not lowercase."), data=data) log.info("User %s merged account %s into %s.", user.username, other.username, user.username) user.merge(other) #Redirect user to the root of the server instead of the settings page return 'json:', dict(success=True, location="/") else: return 'json:', dict(success=False, message=_("Form does not exist."), location="/") return 'json:', dict(success=True, location="/account/settings")
def __default__(self, group, endpoint, token=None, anonymous=None, **kw): from brave.core.application.model import ApplicationGrant anonymous = None if anonymous is None else boolean(anonymous) log.info( "service={0!r} group={1} endpoint={2} token={3} anonymous={4} data={5}" .format(request.service, group, endpoint, token, anonymous, kw)) # Prevent access to certain overly-broad API calls. if group == 'account' and endpoint != 'AccountStatus': return dict( success=False, reason='endpoint.restricted', message="Access restricted to endpoint: {0}.{1}".format( group, endpoint)) try: # Get the appropriate grant. token = ApplicationGrant.objects.get( id=token, application=request.service) if token else None except ApplicationGrant.DoesNotExist: return dict(success=False, reason='grant.invalid', message="Application grant invalid or expired.") try: # Load the API endpoint. call = getattr(getattr(api, group, None), endpoint) except AttributeError: return dict(success=False, reason='endpoint.invalid', message="Unknown API endpoint: {0}.{1}".format( group, endpoint)) key = None if anonymous is False or token or call.mask: # Check that this grant allows calls to this API endpoint. if call.mask and (not token or not token.mask.has_access(call.mask)): return dict( success=False, reason='grant.unauthorized', message="Not authorized to call endpoint: {0}.{1}".format( group, endpoint)) # Find an appropriate key to use for this request if one is required or anonymous=False. key = token.character.credential_for(call.mask) if not key: return dict( success=False, reason='key.notfound', message= "Could not find EVE API key that authorizes endpoint: {0}.{1}" .format(group, endpoint)) try: # Perform the query or get the cached result. result = call(key, **kw) if key else call(**kw) except Exception as e: log.exception("Unable to process request.") return dict( success=False, reason='eve.unknown', message="Encountered unexpected error during EVE API call.", detail=unicode(e)) # Ensure the presence of a 'success' key for quick validation of calls and return the result. result.update(success=True) return result
def native(self, value): if isinstance(value, list): return boolean(value[-1]) return boolean(value)
def assert_boolean(boolean, expected): assert conv.boolean(boolean) is expected
def test_boolean_exceptions(self): self.assertRaises(ValueError, lambda: conv.boolean('oui'))
def __call__(self, request): """Non-recursively descend through Controller instances. If we encounter a non-Controller Dialect instance, pass on the modified request. """ last = None part = self trailing = boolean(web.core.config.get('trailing_slashes', True)) while True: last = part part = request.path_info_peek() request.environ['web.controller'] = request.script_name if not part: # If the last object under consideration was a controller, not a method, # attempt to call the index method, then attempt the default, or bail # with a 404 error. if not request.path.endswith('/') and getattr(last, '__trailing_slash__', trailing): location = request.path + '/' + (('?' + request.query_string) if request.query_string else '') log.debug("Trailing slash omitted from path, redirecting to %s.", location) raise web.core.http.HTTPMovedPermanently(location=location) log.debug("No method given, searching for index method.") part = 'index' log.debug("Looking for %r attribute of %r.", part, last) part, request.format = part.rsplit('.', 1) if '.' in part else (part, None) protected = part.startswith('_') try: part = getattr(last, part, None) except UnicodeEncodeError: part = None request.charset = 'utf8' data = request.params.mixed() remaining = request.path_info.strip('/') remaining = remaining.split('/') if remaining else [] if not isinstance(part, Controller) and isinstance(part, Dialect): log.debug("Context switching from Controller to other Dialect instance.") request.path_info_pop() return part(request) # If the URL portion exists as an attribute on the object in question, start searching again on that attribute. if isinstance(part, Controller): log.debug("Continuing descent through controller structure.") request.path_info_pop() continue # If the current object under consideration is a decorated controller method, the search is ended. if hasattr(part, '__call__') and not protected: log.debug("Found callable, passing control. part(%r, %r)", remaining[1:], data) request.path_info_pop() remaining, data = last.__before__(*remaining[1:], **data) return last.__after__(part(*remaining, **data)) fallback = None try: fallback = last.__default__ except AttributeError: pass if fallback: # If the current object under consideration has a “default” method then the search is ended with that method returned. log.debug("Calling default method of %r.", last) remaining, data = last.__before__(*remaining, **data) return last.__after__(fallback(*remaining, **data)) try: fallback = last.__lookup__ except AttributeError: pass if fallback: # If the current object under consideration has a “lookup” method then execute the “lookup” method, and start the search again on the return value of that method. log.debug("Calling lookup method of %r.", last) part, remaining = fallback(*remaining, **data) request.path_info = '/' + '/'.join(remaining) + ('/' if request.path.endswith('/') else '') continue raise web.core.http.HTTPNotFound()
def authenticate(identifier, password): """Given an e-mail address (or Yubikey OTP) and password, authenticate a user.""" ts = time() # Record the query = dict(active=True) # Gracefully handle extended characters in passwords. # The password storage algorithm works in binary. if isinstance(password, unicode): password = password.encode("utf8") # Build the MongoEngine query to find if "@" in identifier: query[b"email"] = identifier elif len(identifier) == 44: query[b"otp"] = identifier[:12] else: query[b"username"] = identifier user = User.objects(**query).first() if ( not user or not User.password.check(user.password, password) or (user.rotp and len(user.otp) != 0 and not "otp" in query) ): if user: LoginHistory(user, False, request.remote_addr).save() # Prevent basic timing attacks; always take at least one second to process. sleep(max(min(1 - (time() - ts), 0), 1)) return None # Validate Yubikey OTP if "otp" in query: client = yubico.Yubico( config["yubico.client"], config["yubico.key"], boolean(config.get("yubico.secure", False)) ) try: status = client.verify(identifier, return_response=True) except: return None if not status: return None user.update(set__seen=datetime.utcnow()) # Record the fact the user signed in. LoginHistory(user, True, request.remote_addr).save() # Update the user's host user.host = request.remote_addr # Check for other accounts with this IP address if len(User.objects(host=request.remote_addr)) > 1: # Quite possibly the worst code ever for u in User.objects(host=request.remote_addr): User.add_duplicate(user, u, IP=True) user.save() return user.id, user
def authorize(self, success=None, failure=None): """Prepare a incoming session request. Error 'message' attributes are temporary; base your logic on the status and code attributes. success: web.core.url:URL (required) failure: web.core.url:URL (required) returns: location: web.core.url:URL the location to direct users to """ # Ensure success and failure URLs are present. if success is None: response.status_int = 400 return dict( status = 'error', code = 'argument.success.missing', message = "URL to return users to upon successful authentication is missing from your request." ) if failure is None: response.status_int = 400 return dict( status = 'error', code = 'argument.failure.missing', message = "URL to return users to upon authentication failure or dismissal is missing from your request." ) # Also ensure they are valid URIs. try: success_ = success success = URL(success) except: response.status_int = 400 return dict( status = 'error', code = 'argument.success.malformed', message = "Successful authentication URL is malformed." ) try: failure_ = failure failure = URL(failure) except: response.status_int = 400 return dict( status = 'error', code = 'argument.response.malformed', message = "URL to return users to upon successful authentication is missing from your request." ) # Deny localhost/127.0.0.1 loopbacks and 192.* and 10.* unless in development mode. if not boolean(config.get('debug', False)) and (success.host in ('localhost', '127.0.0.1') or \ success.host.startswith('192.168.') or \ success.host.startswith('10.')): response.status_int = 400 return dict( status = 'error', code = 'development-only', message = "Loopback and local area-network URLs disallowd in production." ) # Check blacklist and bail early. if AuthenticationBlacklist.objects(reduce(__or__, [ Q(scheme=success.scheme), Q(scheme=failure.scheme), Q(protocol=success.port or success.scheme), Q(protocol=failure.port or failure.scheme), ] + ([] if not success.host else [ Q(domain=success.host) ]) + ([] if not failure.host else [ Q(domain=failure.host) ]))).count(): response.status_int = 400 return dict( status = 'error', code = 'blacklist', message = "You have been blacklisted. To dispute, contact {0}".format(config['mail.blackmail.author']) ) # TODO: Check DNS. Yes, really. # Generate authentication token. log.info("Creating request for {0} with callbacks {1} and {2}.".format(request.service, success_, failure_)) ar = AuthenticationRequest( request.service, # We have an authenticated request, so we know the service ID is valid. success = success_, failure = failure_ ) ar.save() return dict( location = url.complete('/authorize/{0}'.format(ar.id)) )
def authorize(self, success=None, failure=None): """Prepare a incoming session request. Error 'message' attributes are temporary; base your logic on the status and code attributes. success: web.core.url:URL (required) failure: web.core.url:URL (required) returns: location: web.core.url:URL the location to direct users to """ # Ensure success and failure URLs are present. if success is None: response.status_int = 400 return dict( status='error', code='argument.success.missing', message= "URL to return users to upon successful authentication is missing from your request." ) if failure is None: response.status_int = 400 return dict( status='error', code='argument.failure.missing', message= "URL to return users to upon authentication failure or dismissal is missing from your request." ) # Also ensure they are valid URIs. try: success_ = success success = URL(success) except: response.status_int = 400 return dict(status='error', code='argument.success.malformed', message="Successful authentication URL is malformed.") try: failure_ = failure failure = URL(failure) except: response.status_int = 400 return dict( status='error', code='argument.response.malformed', message= "URL to return users to upon successful authentication is missing from your request." ) # Deny localhost/127.0.0.1 loopbacks and 192.* and 10.* unless in development mode. if not boolean(config.get('debug', False)) and (success.host in ('localhost', '127.0.0.1') or \ success.host.startswith('192.168.') or \ success.host.startswith('10.')): response.status_int = 400 return dict( status='error', code='development-only', message= "Loopback and local area-network URLs disallowd in production." ) # Check blacklist and bail early. if AuthenticationBlacklist.objects( reduce(__or__, [ Q(scheme=success.scheme), Q(scheme=failure.scheme), Q(protocol=success.port or success.scheme), Q(protocol=failure.port or failure.scheme), ] + ([] if not success.host else [Q(domain=success.host)]) + ([] if not failure.host else [Q( domain=failure.host)]))).count(): response.status_int = 400 return dict( status='error', code='blacklist', message="You have been blacklisted. To dispute, contact {0}". format(config['mail.blackmail.author'])) # TODO: Check DNS. Yes, really. # Generate authentication token. log.info("Creating request for {0} with callbacks {1} and {2}.".format( request.service, success_, failure_)) ar = AuthenticationRequest( request. service, # We have an authenticated request, so we know the service ID is valid. success=success_, failure=failure_) ar.save() return dict(location=url.complete('/authorize/{0}'.format(ar.id)))