Ejemplo n.º 1
0
    def process_value(self, value, property, prefix=""):
        unique = property.get('unique', True)
        expected_type = property.expected_type.key

        at = {"key": self.key, "property": prefix + property.name}

        if isinstance(value, list):
            if unique is True:
                raise common.BadData(
                    message='expected atom, found list', at=at, value=value
                )

            p = web.storage(property.copy())
            p.unique = True
            return [self.process_value(v, p) for v in value]

        if unique is False:
            raise common.BadData(
                message='expected list, found atom', at=at, value=value
            )

        type_found = common.find_type(value)

        if expected_type in common.primitive_types:
            # string can be converted to any type and int can be converted to float
            try:
                if type_found == '/type/string' and expected_type != '/type/string':
                    value = common.primitive_types[expected_type](value)
                elif type_found == '/type/int' and expected_type == '/type/float':
                    value = float(value)
            except ValueError as e:
                raise common.BadData(message=str(e), at=at, value=value)
        elif property.expected_type.kind == 'embeddable':
            if isinstance(value, dict):
                return self.process_data(
                    value, property.expected_type, prefix=at['property'] + "."
                )
            else:
                raise common.TypeMismatch(expected_type, type_found, at=at, value=value)
        else:
            if type_found == '/type/string':
                value = common.Reference(value)

        type_found = common.find_type(value)

        if type_found == '/type/object':
            type_found = self.get_type(value)

            # type is not found only when the thing id not found.
            if type_found is None:
                raise common.NotFound(key=text_type(value), at=at)

        if expected_type != type_found:
            raise common.BadData(
                message='expected %s, found %s'
                % (property.expected_type.key, type_found),
                at=at,
                value=value,
            )
        return value
Ejemplo n.º 2
0
    def store_account_info(self, username, email, enc_password, data):
        """Store account info in the store so that the account can be created after verifying the email."""
        store = self.site.store.store

        email = email.strip()

        account_key = "account/" + username
        email_key = "account-email/" + email.lower()

        if store.get(account_key):
            raise common.BadData(message="User already exists: %s" % username)

        if store.get(email_key):
            raise common.BadData(message='Email is already used: ' + email)

        now = datetime.utcnow()
        expires_on = now + timedelta(days=14)  # 2 weeks

        account_doc = {
            "_key": account_key,
            "type": "account",
            "status": "pending",
            "created_on": now.isoformat(),
            "username": username,
            "lusername": username.lower(),  # lowercase username
            "email": email,
            "enc_password": enc_password,
            "data": data,
        }
        email_doc = {
            "_key": email_key,
            "type": "account-email",
            "username": username
        }
        store.put_many([account_doc, email_doc])
Ejemplo n.º 3
0
 def validate_properties(self, data):
     rx = web.re_compile('^[a-z][a-z0-9_]*$')
     for key in data:
         if not rx.match(key):
             raise common.BadData(
                 message="Bad Property: %s" % repr(key), at=dict(key=self.key)
             )
Ejemplo n.º 4
0
    def get_user_email(self, username):
        logger.debug("get_user_email", username)

        if username.startswith("/"):
            # this is user key
            userkey = username
            username = username.split("/")[-1]
        else:
            userkey = get_user_root() + username

        details = self.site.store.get_user_details(username)

        logger.debug("get_user_email details %s %s", username, details)

        if details:
            return details.email

        doc = self.site.store.store.get("account/" + username)
        logger.debug("get_user_email doc %s", doc)

        if doc and doc.get("type") == "pending-account":
            return doc['email']

        raise common.BadData(
            message='No user registered with username: ' + username,
            error="account_not_found",
        )
Ejemplo n.º 5
0
    def POST_activate(self, site):
        i = input('username')

        a = site.get_account_manager()
        status = a.activate(i.username)
        if status == "ok":
            return {"ok": "true"}
        else:
            raise common.BadData(error_code=status, message="Account activation failed.")
Ejemplo n.º 6
0
    def check_reset_code(self, username, code):
        SEC_PER_WEEK = 7 * 24 * 3600
        timestamp, code = code.split('$', 1)

        # code is valid only for a week
        if int(timestamp) + SEC_PER_WEEK < int(time.time()):
            raise common.BadData(message='Password Reset code expired')

        username = get_user_root() + username
        details = self.site.store.get_user_details(username)

        if not details:
            raise common.BadData(message="Invalid username")

        text = details.password + '$' + timestamp

        if not self._check_salted_hash(self.secret_key, text, code):
            raise common.BadData(message="Invalid password reset code")
Ejemplo n.º 7
0
    def POST_login(self, site):
        i = input('username', 'password')
        a = site.get_account_manager()
        status = a.login(i.username, i.password)

        if status == "ok":
            a.set_auth_token(get_user_root() + i.username)
            return {"ok": True}
        else:
            raise common.BadData(code=status, message="Login failed")
Ejemplo n.º 8
0
def input(*required, **defaults):
    if 'infobase_input' in web.ctx:
        d = web.ctx.infobase_input
    else:
        d = web.input()

    for k in required:
        if k not in d:
            raise common.BadData(message="Missing argument: " + repr(k))

    result = web.storage(defaults)
    result.update(d)
    return result
Ejemplo n.º 9
0
    def update_user(self, old_password, new_password, email):
        user = self.get_user()
        if user is None:
            raise common.PermissionDenied(message="Not logged in")

        if not self.checkpassword(user.key, old_password):
            raise common.BadData(message='Invalid Password')

        new_password and self.assert_password(new_password)
        email and self.assert_email(email)

        enc_password = new_password and self._generate_salted_hash(
            self.secret_key, new_password)
        self.update_user1(user, enc_password, email)
Ejemplo n.º 10
0
    def POST_update_user(self, site):
        i = input('old_password', new_password=None, email=None)
        a = site.get_account_manager()

        user = a.get_user()
        username = user.key.split("/")[-1]

        status = a.login(username, i.old_password)
        if status == "ok":
            kw = {}
            if i.new_password:
                kw['password'] = i.new_password
            if i.email:
                kw['email'] = i.email
            a.update(username, **kw)
        else:
            raise common.BadData(code=status, message="Invalid password")
Ejemplo n.º 11
0
    def _process(self, key, data, prev_data=None):
        self.key = key  # hack to make key available when raising exceptions.

        if 'key' not in data:
            data['key'] = key

        if web.ctx.get('infobase_bootstrap', False):
            return data

        assert data['key'] == key

        data = common.parse_query(data)
        self.validate_properties(data)
        prev_data = prev_data and common.parse_query(prev_data)

        if not web.ctx.get(
            'disable_permission_check', False
        ) and not self.has_permission(self.author, key):
            raise common.PermissionDenied(
                message='Permission denied to modify %s' % repr(key)
            )

        type = data.get('type')
        if type is None:
            raise common.BadData(message="missing type", at=dict(key=key))
        type = self.process_value(type, self.get_property(None, 'type'))
        type = self.get_thing(type)

        # when type is changed, consider as all object is modified and don't compare with prev data.
        if prev_data and prev_data.get('type') != type.key:
            prev_data = None

        data = self.process_data(data, type, prev_data)

        for k in common.READ_ONLY_PROPERTIES:
            data.pop(k, None)
            prev_data and prev_data.pop(k, None)

        if data == prev_data:
            return None
        else:
            return data
Ejemplo n.º 12
0
def to_int(value, key):
    try:
        return int(value)
    except:
        raise common.BadData(
            message=f"Bad integer value for {repr(key)}: {repr(value)}")
Ejemplo n.º 13
0
def from_json(s):
    try:
        return json.loads(s)
    except ValueError as e:
        raise common.BadData(message="Bad JSON: " + str(e))
Ejemplo n.º 14
0
def to_int(value, key):
    try:
        return int(value)
    except:
        raise common.BadData(message="Bad integer value for %s: %s" %
                             (repr(key), repr(value)))
Ejemplo n.º 15
0
 def assert_type_required(self):
     type_required = any(c.key not in common.COMMON_PROPERTIES
                         for c in self.conditions
                         if not isinstance(c, Query))
     if type_required and self.get_type() is None:
         raise common.BadData(message="missing 'type' in query")
Ejemplo n.º 16
0
    def things(self, query):
        type = query.get_type()
        if type:
            type_metedata = self.get_metadata(type)
            if type_metedata:
                type_id = type_metedata.id
            else:
                # Return empty result when type not found
                return []
        else:
            type_id = None

        # type is required if there are conditions/sort on keys other than [key, type, created, last_modified]
        common_properties = ['key', 'type', 'created', 'last_modified']
        _sort = query.sort and query.sort.key
        if _sort and _sort.startswith('-'):
            _sort = _sort[1:]
        type_required = bool([
            c for c in query.conditions if c.key not in common_properties
        ]) or (_sort and _sort not in common_properties)

        if type_required and type is None:
            raise common.BadData("Type Required")

        class DBTable:
            def __init__(self, name, label=None):
                self.name = name
                self.label = label or name

            def sql(self):
                if self.label != self.name:
                    return "%s as %s" % (self.name, self.label)
                else:
                    return self.name

            def __repr__(self):
                return self.label

        class Literal:
            def __init__(self, value):
                self.value = value

            def __repr__(self):
                return self.value

        tables = {}

        def get_table(datatype, key):
            if key not in tables:
                assert type is not None, "Missing type"
                table = self.schema.find_table(type, datatype, key)
                label = 'd%d' % len(tables)
                tables[key] = DBTable(table, label)
            return tables[key]

        wheres = []

        def process(c, ordering_func=None):
            # ordering_func is used when the query contains emebbabdle objects
            #
            # example: {'links': {'title: 'foo', 'url': 'http://example.com/foo'}}
            if c.datatype == 'ref':
                metadata = self.get_metadata(c.value)
                if metadata is None:
                    # required object is not found so the query result will be empty.
                    # Raise StopIteration to indicate empty result.
                    raise StopIteration
                c.value = metadata.id
            if c.op == '~':
                op = Literal('LIKE')
                c.value = c.value.replace('*', '%').replace('_', r'\_')
            else:
                op = Literal(c.op)

            if c.key in ['key', 'type', 'created', 'last_modified']:
                #@@ special optimization to avoid join with thing.type when there are non-common properties in the query.
                #@@ Since type information is already present in property table,
                #@@ getting property id is equivalent to join with type.
                if c.key == 'type' and type_required:
                    return

                if isinstance(c.value, list):
                    q = web.sqlors('thing.%s %s ' % (c.key, op), c.value)
                else:
                    q = web.reparam('thing.%s %s $c.value' % (c.key, op),
                                    locals())
                xwheres = [q]

                # Add thing table explicitly because get_table is not called
                tables['_thing'] = DBTable("thing")
            else:
                table = get_table(c.datatype, c.key)
                key_id = self.get_property_id(type, c.key)
                if not key_id:
                    raise StopIteration

                q1 = web.reparam('%(table)s.key_id=$key_id' % {'table': table},
                                 locals())

                if isinstance(c.value, list):
                    q2 = web.sqlors('%s.value %s ' % (table, op), c.value)
                else:
                    q2 = web.reparam('%s.value %s $c.value' % (table, op),
                                     locals())

                xwheres = [q1, q2]
                if ordering_func:
                    xwheres.append(ordering_func(table))
            wheres.extend(xwheres)

        def make_ordering_func():
            d = web.storage(table=None)

            def f(table):
                d.table = d.table or table
                if d.table == table:
                    # avoid a comparison when both tables are same. it fails when ordering is None
                    return "1 = 1"
                else:
                    return '%s.ordering = %s.ordering' % (table, d.table)

            return f

        import readquery

        def process_query(q, ordering_func=None):
            for c in q.conditions:
                if isinstance(c, readquery.Query):
                    process_query(c, ordering_func or make_ordering_func())
                else:
                    process(c, ordering_func)

        def process_sort(query):
            """Process sort field in the query and returns the db column to order by."""
            if query.sort:
                sort_key = query.sort.key
                if sort_key.startswith('-'):
                    ascending = " desc"
                    sort_key = sort_key[1:]
                else:
                    ascending = ""

                if sort_key in ['key', 'type', 'created', 'last_modified']:
                    order = 'thing.' + sort_key  # make sure c.key is valid
                    # Add thing table explicitly because get_table is not called
                    tables['_thing'] = DBTable("thing")
                else:
                    table = get_table(query.sort.datatype, sort_key)
                    key_id = self.get_property_id(type, sort_key)
                    if key_id is None:
                        raise StopIteration
                    q = '%(table)s.key_id=$key_id' % {'table': table}
                    wheres.append(web.reparam(q, locals()))
                    order = table.label + '.value'
                return order + ascending
            else:
                return None

        try:
            process_query(query)
            # special care for case where query {}.
            if not tables:
                tables['_thing'] = DBTable('thing')
            order = process_sort(query)
        except StopIteration:
            return []

        def add_joins():
            labels = [t.label for t in tables.values()]

            def get_column(table):
                if table == 'thing': return 'thing.id'
                else: return table + '.thing_id'

            if len(labels) > 1:
                x = labels[0]
                xwheres = [
                    get_column(x) + ' = ' + get_column(y) for y in labels[1:]
                ]
                wheres.extend(xwheres)

        add_joins()
        wheres = wheres or ['1 = 1']
        table_names = [t.sql() for t in tables.values()]

        t = self.db.transaction()
        if config.query_timeout:
            self.db.query(
                "SELECT set_config('statement_timeout', $query_timeout, false)",
                dict(query_timeout=config.query_timeout))

        if 'thing' in table_names:
            result = self.db.select(
                what='thing.key',
                tables=table_names,
                where=self.sqljoin(wheres, ' AND '),
                order=order,
                limit=query.limit,
                offset=query.offset,
            )
            keys = [r.key for r in result]
        else:
            result = self.db.select(
                what='d0.thing_id',
                tables=table_names,
                where=self.sqljoin(wheres, ' AND '),
                order=order,
                limit=query.limit,
                offset=query.offset,
            )
            ids = [r.thing_id for r in result]
            rows = ids and self.db.query(
                'SELECT id, key FROM thing where id in $ids',
                vars={"ids": ids})
            d = dict((r.id, r.key) for r in rows)
            keys = [d[id] for id in ids]
        t.commit()
        return keys
Ejemplo n.º 17
0
    def register1(self,
                  username,
                  email,
                  enc_password,
                  data,
                  ip=None,
                  timestamp=None):
        ip = ip or web.ctx.ip
        key = get_user_root() + username
        if self.site.get(key):
            raise common.BadData(message="User already exists: " + username)

        if self.site.store.find_user(email):
            raise common.BadData(message='Email is already used: ' + email)

        def f():
            web.ctx.disable_permission_check = True

            d = web.storage({"key": key, "type": {"key": "/type/user"}})
            d.update(data)
            self.site.save(key,
                           d,
                           timestamp=timestamp,
                           author=d,
                           comment="Created new account")

            q = make_query(d)
            account_bot = config.get('account_bot')
            account_bot = account_bot and web.storage({
                "key": account_bot,
                "type": {
                    "key": "/type/user"
                }
            })
            self.site.save_many(
                q,
                ip=ip,
                timestamp=timestamp,
                author=account_bot,
                action='register',
                comment="Setup new account",
            )
            self.site.store.register(key, email, enc_password)
            self.update_user_details(username, verified=True, active=True)

            # Add account doc to store
            olddoc = self.site.store.store.get("account/" + username) or {}

            doc = {
                "_key": "account/" + username,
                "_rev": olddoc.get("_rev"),
                "type": "account",
                "registered_on": olddoc['registered_on'],
                "activated_on": timestamp.isoformat(),
                "last_login": timestamp.isoformat(),
            }
            self.site.store.store.put("account/" + username, doc)

        timestamp = timestamp or datetime.utcnow()
        self.site.store.transact(f)

        event_data = dict(data,
                          username=username,
                          email=email,
                          password=enc_password)
        self.site._fire_event(
            "register",
            timestamp=timestamp,
            ip=ip or web.ctx.ip,
            username=None,
            data=event_data,
        )

        self.set_auth_token(key)
        return username