Exemplo n.º 1
0
Arquivo: eve.py Projeto: Acen/core
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
Exemplo n.º 2
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()
Exemplo n.º 3
0
 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
Exemplo n.º 4
0
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
Exemplo n.º 5
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()
Exemplo n.º 6
0
 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
         )
Exemplo n.º 7
0
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")
Exemplo n.º 8
0
Arquivo: sa.py Projeto: Hazer/WebCore
    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)
Exemplo n.º 9
0
    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)
Exemplo n.º 10
0
    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)
Exemplo n.º 11
0
 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)
Exemplo n.º 12
0
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
Exemplo n.º 13
0
    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)
Exemplo n.º 14
0
    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
            )
Exemplo n.º 15
0
Arquivo: eve.py Projeto: Acen/core
 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
Exemplo n.º 16
0
    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
Exemplo n.º 17
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
Exemplo n.º 18
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
Exemplo n.º 19
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")
Exemplo n.º 20
0
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])
Exemplo n.º 21
0
    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")
Exemplo n.º 22
0
    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")
Exemplo n.º 23
0
    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
Exemplo n.º 24
0
Arquivo: form.py Projeto: Acen/core
 def native(self, value):
     if isinstance(value, list):
         return boolean(value[-1])
     return boolean(value)
Exemplo n.º 25
0
def assert_boolean(boolean, expected):
    assert conv.boolean(boolean) is expected
Exemplo n.º 26
0
 def test_boolean_exceptions(self):
     self.assertRaises(ValueError, lambda: conv.boolean('oui'))
Exemplo n.º 27
0
    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()
Exemplo n.º 28
0
def assert_boolean(boolean, expected):
    assert conv.boolean(boolean) is expected
Exemplo n.º 29
0
 def test_boolean_exceptions(self):
     self.assertRaises(ValueError, lambda: conv.boolean('oui'))
Exemplo n.º 30
0
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
Exemplo n.º 31
0
 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))
         )
Exemplo n.º 32
0
    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)))
Exemplo n.º 33
0
 def native(self, value):
     if isinstance(value, list):
         return boolean(value[-1])
     return boolean(value)