Beispiel #1
0
class Publisher(model.Model):  # key_name=urlname
    """Model for a VertNet data Publisher."""
    name = model.StringProperty('n', required=True)
    urlname = model.ComputedProperty(lambda self: urlname(self.name))
    owner = model.UserProperty('o', required=True)
    admins = model.UserProperty('a', repeated=True)
    created = model.DateTimeProperty('c', auto_now_add=True)
    updated = model.DateTimeProperty('u', auto_now=True)
    json = model.TextProperty('j', required=True)  # JSON representation

    @classmethod
    def create(cls, name):
        return Publisher(
            id=urlname(name),
            name=name,
            owner=users.get_current_user(),
            json=simplejson.dumps(dict(
                    url='http://%s.%s.appspot.com/publishers/%s' % \
                        (appver, appid, urlname(name)),
                    name=name,
                    admin=users.get_current_user().nickname())))

    @classmethod
    def get_by_urlname(cls, urlname):
        return model.Key('Publisher', urlname).get()
Beispiel #2
0
class Collection(model.Model):  # key_name=urlname, parent=Publisher
    """Model for a collection of records."""
    name = model.StringProperty('n', required=True)
    urlname = model.ComputedProperty(lambda self: urlname(self.name))
    owner = model.UserProperty('o', required=True)
    admins = model.UserProperty('a', repeated=True)
    created = model.DateTimeProperty('c', auto_now_add=True)
    updated = model.DateTimeProperty('u', auto_now=True)
    json = model.TextProperty('j', required=True)  # JSON representation

    @classmethod
    def create(cls, name, publisher_key):
        return Collection(
            parent=publisher_key,
            id=urlname(name),
            name=name,
            owner=users.get_current_user(),
            json=simplejson.dumps(dict(
                    name=name,
                    admin=users.get_current_user().nickname(),
                    url='http://%s.%s.appspot.com/publishers/%s/%s' % \
                        (appver, appid, publisher_key.id(), urlname(name)))))

    @classmethod
    def get_by_urlname(cls, urlname, publisher_key):
        return model.Key('Collection', urlname, parent=publisher_key).get()

    @classmethod
    def all_by_publisher(cls, publisher_key):
        return Collection.query(ancestor=publisher_key).fetch()
Beispiel #3
0
class UserToken(model.Model):
    """Stores validation tokens for users."""

    created = model.DateTimeProperty(auto_now_add=True)
    updated = model.DateTimeProperty(auto_now=True)
    user = model.StringProperty(required=True, indexed=False)
    subject = model.StringProperty(required=True)
    token = model.StringProperty(required=True)

    @classmethod
    def get_key(cls, user, subject, token):
        """Returns a token key."""
        return model.Key(cls, '%s.%s.%s' % (user, subject, token))

    @classmethod
    def create(cls, user, subject, token=None):
        """Fetches a user token."""
        token = token or security.generate_random_string(entropy=64)
        key = cls.get_key(user, subject, token)
        entity = cls(key=key, user=user, subject=subject, token=token)
        entity.put()
        return entity

    @classmethod
    def get(cls, user=None, subject=None, token=None):
        """Fetches a user token."""
        if user and subject and token:
            return cls.get_key(user, subject, token).get()

        assert subject and token, \
            'subject and token must be provided to UserToken.get().'
        return cls.query(cls.subject == subject, cls.token == token).get()
Beispiel #4
0
class Session(model.Model):
    """A model to store session data."""

    #: Save time.
    updated = model.DateTimeProperty(auto_now=True)
    #: Session data, pickled.
    data = PickleProperty()

    @classmethod
    def get_by_sid(cls, sid):
        """Returns a ``Session`` instance by session id.

        :param sid:
            A session id.
        :returns:
            An existing ``Session`` entity.
        """
        data = memcache.get(sid)
        if not data:
            session = model.Key(cls, sid).get()
            if session:
                data = session.data
                memcache.set(sid, data)

        return data

    def _put(self):
        """Saves the session and updates the memcache entry."""
        memcache.set(self._key.id(), self.data)
        super(Session, self).put()
Beispiel #5
0
class Record(model.Model):  # key_name=record.occurrenceid, parent=Collection
    """Model for a record."""
    owner = model.UserProperty('u', required=True)
    record = model.TextProperty(
        'r', required=True)  # darwin core json representation
    created = model.DateTimeProperty('c', auto_now_add=True)
    updated = model.DateTimeProperty('u', auto_now=True)

    @classmethod
    def create(cls, rec, collection_key):
        return Record(parent=collection_key,
                      id=rec['occurrenceid'],
                      owner=users.get_current_user(),
                      record=simplejson.dumps(rec))

    @classmethod
    def all_by_collection(cls, collection_key):
        return Record.query(ancestor=collection_key).fetch()
Beispiel #6
0
class CachedTile(model.Model):
    tile = model.BlobKeyProperty(required=True)
    rendered = model.DateTimeProperty(required=True)
    operation_cost = model.IntegerProperty(required=True)
    render_time = model.FloatProperty(required=True)
    level = model.IntegerProperty(required=True)

    #_use_datastore = False
    _use_memcache = False

    @property
    def position(self):
        """Returns the level/x/y tuple for this tile."""
        return tuple(int(x) for x in self.key.id().split('/')[1:])

    @classmethod
    def key_for_tile(cls, type, level, x, y):
        return model.Key(cls, '%s/%s/%s/%s' % (type, level, x, y))
Beispiel #7
0
class TestArticle(model.Model):
    title = model.StringProperty()
    description = model.StringProperty()
    created = model.DateTimeProperty(auto_now=True)

    @classmethod
    def add_property(cls, name, property, **kwargs):
        """
        Method for dynamical adding model properties
        """
        setattr(cls, name, property(name, **kwargs))
        cls._fix_up_properties()

    @classmethod
    def del_property(cls, name):
        """
        Method for dynamical adding model properties
        """
        delattr(cls, name)
        cls._fix_up_properties()
Beispiel #8
0
class Session(model.Model):
    """A model to store session data."""

    #: Save time.
    updated = model.DateTimeProperty(auto_now=True)
    #: Session data, pickled.
    try:
        # If model.PickleProperty exists, we must use it, since the
        # above PickledProperty is broken in NDB 0.9.4 and later.
        # This still leaves NDB 0.9.4 broken, since it doesn't have
        # model.PickleProperty.  Fortunately no SDK contains NDB
        # 0.9.4, we went straight from 0.9.3 (in SDK 1.6.1) to 0.9.6
        # (in SDK 1.6.2).
        data = model.PickleProperty()
    except AttributeError:
        # In NDB 0.9.3 or before, the above PickledProperty works.
        data = PickledProperty(dict)

    @classmethod
    def get_by_sid(cls, sid):
        """Returns a ``Session`` instance by session id.

        :param sid:
            A session id.
        :returns:
            An existing ``Session`` entity.
        """
        data = memcache.get(sid)
        if not data:
            session = model.Key(cls, sid).get()
            if session:
                data = session.data
                memcache.set(sid, data)

        return data

    def _put(self):
        """Saves the session and updates the memcache entry."""
        memcache.set(self._key.id(), self.data)
        super(Session, self).put()
class MigrationEntry(model.Model):
    """
    Represents Migration in storage.
    """
    id = model.StringProperty()
    application = model.StringProperty()
    ctime = model.DateTimeProperty(auto_now_add=True)

    status = model.StringProperty(required=True,
                                  choices=["apply in process",
                                           "rollback in process",
                                           "apply failed",
                                           "rollback failed",
                                           "apply success",
                                           "rollback success",
                                           ])

    @classmethod
    def _pre_delete_hook(cls, key):
        memcache.delete(key.kind())

    def _post_put_hook(self, future):
        super(MigrationEntry, self)._post_put_hook(future)
        memcache.delete(self.key.kind())
Beispiel #10
0
class User(model.Expando):
    """Stores user authentication credentials or authorization ids."""

    #: The model used to ensure uniqueness.
    unique_model = Unique
    #: The model used to store tokens.
    token_model = UserToken

    created = model.DateTimeProperty(auto_now_add=True)
    updated = model.DateTimeProperty(auto_now=True)
    # ID for third party authentication, e.g. 'google:username'. UNIQUE.
    auth_ids = model.StringProperty(repeated=True)
    # Hashed password. Not required because third party authentication
    # doesn't use password.
    password = model.StringProperty()

    def get_id(self):
        """Returns this user's unique ID, which can be an integer or string."""
        return self._key.id()

    @classmethod
    def get_by_auth_id(cls, auth_id):
        """Returns a user object based on a auth_id.

        :param auth_id:
            String representing a unique id for the user. Examples:

            - own:username
            - google:username
        :returns:
            A user object.
        """
        return cls.query(cls.auth_ids == auth_id).get()

    @classmethod
    def get_by_auth_token(cls, user_id, token):
        """Returns a user object based on a user ID and token.

        :param user_id:
            The user_id of the requesting user.
        :param token:
            The token string to be verified.
        :returns:
            A tuple ``(User, timestamp)``, with a user object and
            the token timestamp, or ``(None, None)`` if both were not found.
        """
        token_key = cls.token_model.get_key(user_id, 'auth', token)
        user_key = model.Key(cls, user_id)
        # Use get_multi() to save a RPC call.
        valid_token, user = model.get_multi([token_key, user_key])
        if valid_token and user:
            timestamp = int(time.mktime(valid_token.created.timetuple()))
            return user, timestamp

        return None, None

    @classmethod
    def get_by_auth_password(cls, auth_id, password):
        """Returns a user object, validating password.

        :param auth_id:
            Authentication id.
        :param password:
            Password to be checked.
        :returns:
            A user object, if found and password matches.
        :raises:
            ``auth.InvalidAuthIdError`` or ``auth.InvalidPasswordError``.
        """
        user = cls.get_by_auth_id(auth_id)
        if not user:
            raise auth.InvalidAuthIdError()

        if not security.check_password_hash(password, user.password):
            raise auth.InvalidPasswordError()

        return user

    @classmethod
    def validate_token(cls, user_id, subject, token):
        """Checks for existence of a token, given user_id, subject and token.

        :param user_id:
            User unique ID.
        :param subject:
            The subject of the key. Examples:

            - 'auth'
            - 'signup'
        :param token:
            The token string to be validated.
        :returns:
            A :class:`UserToken` or None if the token does not exist.
        """
        return cls.token_model.get(user=user_id, subject=subject,
                                   token=token) is not None

    @classmethod
    def create_auth_token(cls, user_id):
        """Creates a new authorization token for a given user ID.

        :param user_id:
            User unique ID.
        :returns:
            A string with the authorization token.
        """
        return cls.token_model.create(user_id, 'auth').token

    @classmethod
    def validate_auth_token(cls, user_id, token):
        return cls.validate_token(user_id, 'auth', token)

    @classmethod
    def delete_auth_token(cls, user_id, token):
        """Deletes a given authorization token.

        :param user_id:
            User unique ID.
        :param token:
            A string with the authorization token.
        """
        cls.token_model.get_key(user_id, 'auth', token).delete()

    @classmethod
    def create_signup_token(cls, user_id):
        entity = cls.token_model.create(user_id, 'signup')
        return entity.token

    @classmethod
    def validate_signup_token(cls, user_id, token):
        return cls.validate_token(user_id, 'signup', token)

    @classmethod
    def delete_signup_token(cls, user_id, token):
        cls.token_model.get_key(user_id, 'signup', token).delete()

    @classmethod
    def create_user(cls, auth_id, unique_properties=None, **user_values):
        """Creates a new user.

        :param auth_id:
            A string that is unique to the user. Users may have multiple
            auth ids. Example auth ids:

            - own:username
            - own:[email protected]
            - google:username
            - yahoo:username

            The value of `auth_id` must be unique.
        :param unique_properties:
            Sequence of extra property names that must be unique.
        :param user_values:
            Keyword arguments to create a new user entity. Since the model is
            an ``Expando``, any provided custom properties will be saved.
            To hash a plain password, pass a keyword ``password_raw``.
        :returns:
            A tuple (boolean, info). The boolean indicates if the user
            was created. If creation succeeds, ``info`` is the user entity;
            otherwise it is a list of duplicated unique properties that
            caused creation to fail.
        """
        assert user_values.get('password') is None, \
            'Use password_raw instead of password to create new users.'

        assert not isinstance(auth_id, list), \
            'Creating a user with multiple auth_ids is not allowed, ' \
            'please provide a single auth_id.'

        if 'password_raw' in user_values:
            user_values['password'] = security.generate_password_hash(
                user_values.pop('password_raw'), length=12)

        user_values['auth_ids'] = [auth_id]
        user = cls(**user_values)

        # Set up unique properties.
        uniques = [('%s.auth_id:%s' % (cls.__name__, auth_id), 'auth_id')]
        if unique_properties:
            for name in unique_properties:
                key = '%s.%s:%s' % (cls.__name__, name, user_values[name])
                uniques.append((key, name))

        ok, existing = cls.unique_model.create_multi(k for k, v in uniques)
        if ok:
            user.put()
            return True, user
        else:
            properties = [v for k, v in uniques if k in existing]
            return False, properties
Beispiel #11
0
class UserToken(model.Model):
    """Stores validation tokens for users."""

    created = model.DateTimeProperty(auto_now_add=True)
    updated = model.DateTimeProperty(auto_now=True)
    user = model.StringProperty(required=True, indexed=False)
    subject = model.StringProperty(required=True)
    token = model.StringProperty(required=True)

    @classmethod
    def get_key(cls, user, subject, token):
        """Returns a token key.

        :param user:
            User unique ID.
        :param subject:
            The subject of the key. Examples:

            - 'auth'
            - 'signup'
        :param token:
            Randomly generated token.
        :returns:
            ``model.Key`` containing a string id in the following format:
            ``{user_id}.{subject}.{token}.``
        """
        return model.Key(cls, '%s.%s.%s' % (str(user), subject, token))

    @classmethod
    def create(cls, user, subject, token=None):
        """Creates a new token for the given user.

        :param user:
            User unique ID.
        :param subject:
            The subject of the key. Examples:

            - 'auth'
            - 'signup'
        :param token:
            Optionally an existing token may be provided.
            If None, a random token will be generated.
        :returns:
            The newly created :class:`UserToken`.
        """
        user = str(user)
        token = token or security.generate_random_string(entropy=128)
        key = cls.get_key(user, subject, token)
        entity = cls(key=key, user=user, subject=subject, token=token)
        entity.put()
        return entity

    @classmethod
    def get(cls, user=None, subject=None, token=None):
        """Fetches a user token.

        :param user:
            User unique ID.
        :param subject:
            The subject of the key. Examples:

            - 'auth'
            - 'signup'
        :param token:
            The existing token needing verified.
        :returns:
            A :class:`UserToken` or None if the token does not exist.
        """
        if user and subject and token:
            return cls.get_key(user, subject, token).get()

        assert subject and token, \
            'subject and token must be provided to UserToken.get().'
        return cls.query(cls.subject == subject, cls.token == token).get()
Beispiel #12
0
class User(model.Model):
    """"""

    created = model.DateTimeProperty(auto_now_add=True)
    updated = model.DateTimeProperty(auto_now=True)
    # Display name: username as typed by the user.
    name = model.StringProperty(required=True)
    # Username in lower case. UNIQUE.
    username = model.StringProperty(required=True)
    # ID for third party authentication, e.g. 'google:username'. UNIQUE.
    auth_id = model.StringProperty(required=True)
    # Primary email address. Optionally UNIQUE.
    email = model.StringProperty(required=True)
    # Hashed password. Not required because third party authentication
    # doesn't use password.
    password = model.StringProperty()

    @classmethod
    def get_key(cls, auth_id):
        return model.Key(cls, auth_id.lower())

    @classmethod
    def get_by_auth_id(cls, auth_id):
        return cls.get_key(auth_id).get()

    @classmethod
    def get_by_username(cls, username):
        return cls.query(cls.username == username.lower()).get()

    @classmethod
    def get_by_email(cls, email):
        return cls.query(cls.email == email).get()

    @classmethod
    def get_by_auth_token(cls, auth_id, token):
        token_key = UserToken.get_key(auth_id, 'auth', token)
        user_key = cls.get_key(auth_id)
        # Use get_multi() to save a RPC call.
        valid_token, user = model.get_multi([token_key, user_key])
        if valid_token and user:
            timestamp = int(time.mktime(valid_token.created.timetuple()))
            return user, timestamp

        return None, None

    @classmethod
    def get_by_auth_password(cls, auth_id, password):
        """Returns user, validating password.

        :raises:
            ``auth.InvalidAuthIdError`` or ``auth.InvalidPasswordError``.
        """
        user = cls.get_by_auth_id(auth_id)
        if not user:
            raise auth.InvalidAuthIdError()

        if not security.check_password_hash(password, user.password):
            raise auth.InvalidPasswordError()

        return user

    @classmethod
    def validate_token(cls, auth_id, subject, token):
        return UserToken.get(user=auth_id, subject=subject,
                             token=token) is not None

    @classmethod
    def create_auth_token(cls, auth_id):
        return UserToken.create(auth_id, 'auth').token

    @classmethod
    def validate_auth_token(cls, auth_id, token):
        return cls.validate_token(auth_id, 'auth', token)

    @classmethod
    def delete_auth_token(cls, auth_id, token):
        UserToken.get_key(auth_id, 'auth', token).delete()

    @classmethod
    def create_signup_token(cls, auth_id):
        entity = UserToken.create(auth_id, 'signup')
        return entity.token

    @classmethod
    def validate_signup_token(cls, auth_id, token):
        return cls.validate_token(auth_id, 'signup', token)

    @classmethod
    def delete_signup_token(cls, auth_id, token):
        UserToken.get_key(auth_id, 'signup', token).delete()

    @classmethod
    def create_user(cls, _unique_email=True, **user_values):
        """Creates a new user.

        :param _unique_email:
            True to require the email to be unique, False otherwise.
        :param user_values:
            Keyword arguments to create a new user entity. Required ones are:

            - name
            - username
            - auth_id
            - email

            Optional keywords:

            - password_raw (a plain password to be hashed)

            The properties values of `username` and `auth_id` must be unique.
            Optionally, `email` can also be required to be unique.
        :returns:
            A tuple (boolean, info). The boolean indicates if the user
            was created. If creation succeeds,  ``info`` is the user entity;
            otherwise it is a list of duplicated unique properties that
            caused the creation to fail.
        """
        assert user_values.get('password') is None, \
            'Use password_raw instead of password to create new users'

        if 'password_raw' in user_values:
            user_values['password'] = security.generate_password_hash(
                user_values.pop('password_raw'), length=12)

        user_values['username'] = user_values['username'].lower()
        user_values['auth_id'] = user_values['auth_id'].lower()
        user = User(key=cls.get_key(user_values['auth_id']), **user_values)

        # Unique auth id and email.
        unique_username = '******' % user_values['username']
        uniques = [unique_username]
        if _unique_email:
            unique_email = 'User.email:%s' % user_values['email']
            uniques.append(unique_email)
        else:
            unique_email = None

        if uniques:
            success, existing = unique_model.Unique.create_multi(uniques)

        if success:
            txn = lambda: user.put() if not user.key.get() else None
            if model.transaction(txn):
                return True, user
            else:
                unique_model.Unique.delete_multi(uniques)
                return False, ['auth_id']
        else:
            properties = []
            if unique_username in uniques:
                properties.append('username')

            if unique_email in uniques:
                properties.append('email')

            return False, properties
Beispiel #13
0
class User(model.Model):
    """Universal user model. Can be used with App Engine's default users API,
    own auth or third party authentication methods (OpenId, OAuth etc).
    """
    #: Creation date.
    created = model.DateTimeProperty(auto_now_add=True)
    #: Modification date.
    updated = model.DateTimeProperty(auto_now=True)
    #: User defined unique name, also used as key_name.
    username = model.StringProperty(required=True)
    #: Password, only set for own authentication.
    password = model.StringProperty(required=False)
    #: User email
    email = model.StringProperty(required=False)
    # Admin flag.
    is_admin = model.BooleanProperty(default=False)
    #: Authentication identifier according to the auth method in use. Examples:
    #: * own|username
    #: * gae|user_id
    #: * openid|identifier
    #: * twitter|username
    #: * facebook|username
    auth_id = model.StringProperty(repeated=True)
    # Flag to persist the auth across sessions for third party auth.
    auth_remember = model.BooleanProperty(default=False)
    # Auth token, renewed periodically for improved security.
    session_id = model.StringProperty(required=True)
    # Auth token last renewal date.
    session_updated = model.DateTimeProperty(auto_now_add=True)

    @classmethod
    def get_by_username(cls, username):
        return cls.query(cls.username == username).get()

    @classmethod
    def get_by_auth_id(cls, auth_id):
        return cls.query(cls.auth_id == auth_id).get()

    @classmethod
    def create(cls, username, auth_id, **kwargs):
        """Creates a new user and returns it. If the username already exists,
        returns None.

        :param username:
            Unique username.
        :param auth_id:
            Authentication id, according the the authentication method used.
        :param email:
            Unique email address.
        :param kwargs:
            Additional entity attributes.
        :returns:
            The newly created user or None if the username already exists.
        """
        # Assemble the unique scope/value combinations.
        unique_username = '******' % username
        unique_auth_id = 'User.auth_id:%s' % auth_id
        # Create the unique username, auth_id and email.
        uniques = [unique_username, unique_auth_id]
        # TODO add email to parms in tipfy.auth so that
        # we don't have to use kwargs here
        if 'email' in kwargs:
            unique_email = 'User.email:%s' % kwargs['email']
            uniques.append(unique_email)

        success, existing = Unique.create_multi(uniques)

        if success or DEBUG:
            kwargs['username'] = username
            # make this a list so that we can have multiple auth methods.
            kwargs['auth_id'] = [auth_id]
            # Generate an initial session id.
            kwargs['session_id'] = create_session_id()
            if 'password_hash' in kwargs:
                # Password is already hashed.
                kwargs['password'] = kwargs.pop('password_hash')
            elif 'password' in kwargs:
                # Password is not hashed: generate a hash.
                kwargs['password'] = generate_password_hash(kwargs['password'])
            user = cls(**kwargs)
            user.put()
            return user
        else:
            # The ordering her is important. Email must come before
            # auth id or the error return will make little since to the user.
            if unique_email in existing:
                raise UniqueConstraintViolation('Email %s already '
                                                'exists. Try logging in.' %
                                                kwargs['email'])
            if unique_username in existing:
                raise UniqueConstraintViolation('Username %s already '
                                                'exists' % username)

            if unique_auth_id in existing:
                raise UniqueConstraintViolation('Auth id %s already '
                                                'exists' % auth_id)

    def set_password(self, new_password):
        """Sets a new, plain password.

        :param new_password:
            A plain, not yet hashed password.
        :returns:
            None.
        """
        self.password = generate_password_hash(new_password)

    def check_password(self, password):
        """Checks if a password is valid. This is done with form login

        :param password:
            Password to be checked.
        :returns:
            True is the password is valid, False otherwise.
        """
        if check_password_hash(self.password, password):
            return True

        return False

    def check_session(self, session_id):
        """Checks if an auth token is valid.

        :param session_id:
            Token to be checked.
        :returns:
            True is the token id is valid, False otherwise.
        """
        if self.session_id == session_id:
            return True

        return False

    def renew_session(self, force=False, max_age=None):
        """Renews the session id if its expiration time has passed.

        :param force:
            True to force the session id to be renewed, False to check
            if the expiration time has passed.
        :returns:
            None.
        """
        if not force:
            # Only renew the session id if it is too old.
            expires = datetime.timedelta(seconds=max_age)
            force = (self.session_updated + expires < datetime.datetime.now())

        if force:
            self.session_id = create_session_id()
            self.session_updated = datetime.datetime.now()
            self.put()

    def __unicode__(self):
        """Returns this entity's username.

        :returns:
            Username, as unicode.
        """
        return unicode(self.username)

    def __str__(self):
        """Returns this entity's username.

        :returns:
            Username, as unicode.
        """
        return self.__unicode__()

    def __eq__(self, obj):
        """Compares this user entity with another one.

        :returns:
            True if both entities have same key, False otherwise.
        """
        if not obj:
            return False

        return str(self.key) == str(obj.key)

    def __ne__(self, obj):
        """Compares this user entity with another one.

        :returns:
            True if both entities don't have same key, False otherwise.
        """
        return not self.__eq__(obj)