Пример #1
0
    def update(self,
               title=None,
               category=None,
               difficulty=None,
               youtube_id=None,
               revision=None,
               **kwargs):
        """Update the Scratchpad and create a new ScratchpadRevision.

        If the either the Scratchpad or the ScratchpadRevision is invalid, the
        database will be unchanged.

        Arguments:
            revision:
                Dict of ScratchpadRevision properties passed verbatim to
                Scratchpad.create_revision.

            *:
                All other explicit (i.e. not **kwargs) arguments are assigned
                verbatim as properties to the Scratchpad, even if they hold
                a value of None. This is true if either None is explicitly
                passed in, or the default value of None is used.

            kwargs:
                Should be empty. If set, a db.BadKeyError will be thrown since
                someone passed in a field we weren't expecting.

        Note: While all keys have defaults of None, some, like revision, are
              actually required. This is done to to make any incomplete data
              throw a db.BadValueError instead of a TypeError.
        """

        if kwargs:
            raise db.BadKeyError("Unexpected property " + kwargs.keys()[0])

        if revision is None:
            raise db.BadValueError("Property revision is required")

        self.title = title
        self.category = category
        self.difficulty = difficulty
        self.youtube_id = youtube_id

        def save_scratchpad_and_revision():
            self.put()
            # Convert the dict keys to strings in case they're unicode encoded
            # (This happens if revision came from JSON)
            self.create_revision(**dict_keys_to_strings(revision))

        transaction_util.ensure_in_transaction(save_scratchpad_and_revision)

        return self
Пример #2
0
    def delete(self):
        # Override delete so that we can also delete associated Credential
        # models in the same transaction.
        def txn():
            credential = Credential.retrieve_for_user(self)
            if credential:
                credential.delete()

            # Delegate to the base class implementation of db.Model.delete
            # to do the actual deletion of this class.
            super(CredentialedUser, self).delete()

        transaction_util.ensure_in_transaction(txn)
Пример #3
0
    def delete(self):
        # Override delete so that we can also delete associated Credential
        # models in the same transaction.
        def txn():
            credential = Credential.retrieve_for_user(self)
            if credential:
                credential.delete()

            # Delegate to the base class implementation of db.Model.delete
            # to do the actual deletion of this class.
            super(CredentialedUser, self).delete()

        transaction_util.ensure_in_transaction(txn)
Пример #4
0
    def set_password_from_user(self, other_user):
        """ Sets the password for this user to be the same as that of
        another user.

        To be used sparingly! This should only be done for user migrations
        and other admin-related items.

        """
        def txn():
            credential = Credential.retrieve_for_user(other_user)
            cred_copy = None
            if credential:
                cred_copy = Credential(parent=self)
                cred_copy.hashed_pass = credential.hashed_pass
                cred_copy.salt = credential.salt
            self.credential_version = other_user.credential_version
            if cred_copy:
                db.put([cred_copy, self])
            else:
                db.put(self)

        transaction_util.ensure_in_transaction(txn, xg_on=True)
Пример #5
0
    def set_password(self, raw_password):
        """ Updates the password for this user and invalidates previous ones.

        This operation will update this UserData object as well, so any
        outstanding changes on it will be persisted.

        Authentication tokens distributed via auth/tokens.py will also be
        invalidated as a result of this operation (e.g. the user's auth cookie)

        """
        new_cred_version = os.urandom(16).encode('hex')

        def txn():
            c = Credential.retrieve_for_user(self)
            if c is not None:
                c.delete()
            new_cred = Credential.make_for_user(self, raw_password)
            self.credential_version = new_cred_version
            db.put([new_cred, self])

        # The Remote API doesn't support queries inside transactions
        transaction_util.ensure_in_transaction(txn)
Пример #6
0
    def set_password(self, raw_password):
        """ Updates the password for this user and invalidates previous ones.

        This operation will update this UserData object as well, so any
        outstanding changes on it will be persisted.

        Authentication tokens distributed via auth/tokens.py will also be
        invalidated as a result of this operation (e.g. the user's auth cookie)

        """
        new_cred_version = os.urandom(16).encode('hex')

        def txn():
            c = Credential.retrieve_for_user(self)
            if c is not None:
                c.delete()
            new_cred = Credential.make_for_user(self, raw_password)
            self.credential_version = new_cred_version
            db.put([new_cred, self])
            
        # The Remote API doesn't support queries inside transactions
        transaction_util.ensure_in_transaction(txn)
Пример #7
0
    def set_password_from_user(self, other_user):
        """ Sets the password for this user to be the same as that of
        another user.

        To be used sparingly! This should only be done for user migrations
        and other admin-related items.

        """

        def txn():
            credential = Credential.retrieve_for_user(other_user)
            cred_copy = None
            if credential:
                cred_copy = Credential(parent=self)
                cred_copy.hashed_pass = credential.hashed_pass
                cred_copy.salt = credential.salt
            self.credential_version = other_user.credential_version
            if cred_copy:
                db.put([cred_copy, self])
            else:
                db.put(self)

        transaction_util.ensure_in_transaction(txn, xg_on=True)
Пример #8
0
    def post(self):
        valid_token, unverified_user = self.resolve_token()
        user_data = _resolve_user_in_https_frame(self)
        if not valid_token and not user_data:
            logging.warn("No valid token or user for /completesignup")
            self.redirect("/")
            return

        if valid_token:
            if user_data:
                logging.warn("Existing user is signed in, but also specified "
                             "a valid UnverifiedUser's token. Ignoring "
                             " existing sign-in and using token")
            user_data = None

        # Store values in a dict so we can iterate for monotonous checks.
        values = {
            'nickname': self.request_string('nickname', default=None),
            'gender': self.request_string('gender', default="unspecified"),
            'username': self.request_string('username', default=None),
            'password': self.request_string('password', default=None),
        }

        # Simple existence validations
        errors = {}
        for field, error in [('nickname', "Please tell us your name."),
                             ('username', "Please pick a username."),
                             ('password', "We need a password from you.")]:
            if not values[field]:
                errors[field] = error

        gender = None
        if values['gender']:
            gender = values['gender'].lower()
            if gender not in ['male', 'female']:
                gender = None

        if values['username']:
            username = values['username']
            # TODO(benkomalo): ask for advice on text
            if user_models.UniqueUsername.is_username_too_short(username):
                errors['username'] = "******"
            elif not user_models.UniqueUsername.is_valid_username(username):
                errors['username'] = "******"

            # Only check to see if it's available if we're changing values
            # or if this is a brand new UserData
            elif ((not user_data or user_data.username != username) and
                    not user_models.UniqueUsername.is_available_username(username)):
                errors['username'] = "******"

        if values['password']:
            password = values['password']
            if not auth.passwords.is_sufficient_password(password,
                                                         values['nickname'],
                                                         values['username']):
                errors['password'] = "******"


        if len(errors) > 0:
            self.render_json({'errors': errors}, camel_cased=True)
            return

        if user_data:
            # Existing user - update their info
            def txn():
                if (username != user_data.username
                        and not user_data.claim_username(username)):
                    errors['username'] = "******"
                    return False

                user_data.set_password(password)
                user_data.update_nickname(values['nickname'])

            transaction_util.ensure_in_transaction(txn, xg_on=True)
            if len(errors) > 0:
                self.render_json({'errors': errors}, camel_cased=True)
                return

        else:
            # Converting unverified_user to a full UserData.
            num_tries = 0
            user_data = None
            while not user_data and num_tries < 2:
                # Double-check to ensure we don't create any duplicate ids!
                user_id = uid.new_user_id()
                user_data = user_models.UserData.insert_for(
                        user_id,
                        unverified_user.email,
                        username,
                        password,
                        birthdate=unverified_user.birthdate,
                        gender=gender)

                if not user_data:
                    self.render_json({'errors': {'username': "******"}},
                                     camel_cased=True)
                    return
                elif user_data.username != username:
                    # Something went awry - insert_for may have returned
                    # an existing user due to an ID collision. Try again.
                    user_data = None
                num_tries += 1

            if not user_data:
                logging.error("Tried several times to create a new user " +
                              "unsuccessfully")
                self.render_json({
                        'errors': {'username': "******" +
                                               "Please try again later."}
                }, camel_cased=True)
                return

            # Nickname is special since it requires updating external indices.
            user_data.update_nickname(values['nickname'])

            # TODO(benkomalo): move this into a transaction with the above creation
            unverified_user.delete()

        # TODO(benkomalo): give some kind of "congrats"/"welcome" notification
        Login.return_login_json(self, user_data, cont=user_data.profile_root)
Пример #9
0
    def create(title=None,
               category=None,
               difficulty=None,
               youtube_id=None,
               user_id=None,
               origin_scratchpad_id=None,
               origin_revision_id=None,
               revision=None,
               **kwargs):
        """Create a new Scratchpad and ScratchpadRevision and save them to DB

        If either the Scratchpad or the ScratchpadRevision is invalid, the
        database will be unchanged.

        Arguments:
            origin_scratchpad_id:
                The id of the Scratchpad this Scratchpad is based off of.
                Used to set the origin_revision property of the Scratchpad
                being created.

            origin_revision_id:
                The revision of the origin Scratchpad that this Scratchpad is
                based on. Used to set the origin_scratchpad property of the
                Scratchpad being created.

            revision:
                Dict of ScratchpadRevision properties passed verbatim to
                Scratchpad.create_revision to create the initial revision of
                the Scratchpad.

            *:
                All other explicit (i.e. not **kwargs) arguments are passed
                verbatim to the Scratchpad constructor.

            kwargs:
                Should be empty. If set, a db.BadKeyError will be thrown since
                someone passed in a field we weren't expecting.

        Note: While all keys have defaults of None, some, like revision, are
              actually required. This is done to to make any incomplete data
              throw a db.BadValueError instead of a TypeError.
        """
        if kwargs:
            raise db.BadKeyError("Unexpected property " + kwargs.keys()[0])

        scratchpad = Scratchpad(title=title,
                                category=category,
                                difficulty=difficulty,
                                youtube_id=youtube_id,
                                user_id=user_id)

        if origin_revision_id and origin_scratchpad_id:
            origin_scratchpad = Scratchpad.get_by_id(origin_scratchpad_id)

            if origin_scratchpad is None:
                raise db.BadValueError("No scratchpad with id %d" % (
                    origin_scratchpad_id))

            origin_revision = origin_scratchpad.get_revision_by_id(
                origin_revision_id)

            if origin_revision is None:
                raise db.BadValueError(
                    "No revision with id %d for scratchpad %d" % (
                        origin_scratchpad_id, origin_revision_id))

            scratchpad.origin_scratchpad = origin_scratchpad
            scratchpad.origin_revision = origin_revision
        elif origin_revision_id or origin_scratchpad_id:
            raise db.BadValueError(
                "Specified one of origin_scratchpad_id/origin_revision_id"
                " but not the other")

        if revision is None:
            raise db.BadValueError("Property revision is required")

        def save_scratchpad_and_revision():
            scratchpad.put()
            # Convert the dict keys to strings in case they're unicode encoded
            # (This happens if revision came from JSON)
            scratchpad.create_revision(**dict_keys_to_strings(revision))

        transaction_util.ensure_in_transaction(save_scratchpad_and_revision)

        return scratchpad
Пример #10
0
    def post(self):
        valid_token, unverified_user = self.resolve_token()
        user_data = _resolve_user_in_https_frame(self)
        if not valid_token and not user_data:
            logging.warn("No valid token or user for /completesignup")
            self.redirect("/")
            return

        if valid_token:
            if user_data:
                logging.warn("Existing user is signed in, but also specified "
                             "a valid UnverifiedUser's token. Ignoring "
                             " existing sign-in and using token")
            user_data = None

        # Store values in a dict so we can iterate for monotonous checks.
        values = {
            'nickname': self.request_string('nickname', default=None),
            'gender': self.request_string('gender', default="unspecified"),
            'username': self.request_string('username', default=None),
            'password': self.request_string('password', default=None),
        }

        # Simple existence validations
        errors = {}
        for field, error in [('nickname', "Please tell us your name."),
                             ('username', "Please pick a username."),
                             ('password', "We need a password from you.")]:
            if not values[field]:
                errors[field] = error

        gender = None
        if values['gender']:
            gender = values['gender'].lower()
            if gender not in ['male', 'female']:
                gender = None

        if values['username']:
            username = values['username']
            # TODO(benkomalo): ask for advice on text
            if user_models.UniqueUsername.is_username_too_short(username):
                errors['username'] = "******"
            elif not user_models.UniqueUsername.is_valid_username(username):
                errors[
                    'username'] = "******"

            # Only check to see if it's available if we're changing values
            # or if this is a brand new UserData
            elif ((not user_data or user_data.username != username)
                  and not user_models.UniqueUsername.is_available_username(
                      username)):
                errors['username'] = "******"

        if values['password']:
            password = values['password']
            if not auth.passwords.is_sufficient_password(
                    password, values['nickname'], values['username']):
                errors['password'] = "******"

        if len(errors) > 0:
            self.render_json({'errors': errors}, camel_cased=True)
            return

        if user_data:
            # Existing user - update their info
            def txn():
                if (username != user_data.username
                        and not user_data.claim_username(username)):
                    errors['username'] = "******"
                    return False

                user_data.set_password(password)
                user_data.update_nickname(values['nickname'])

            transaction_util.ensure_in_transaction(txn, xg_on=True)
            if len(errors) > 0:
                self.render_json({'errors': errors}, camel_cased=True)
                return

        else:
            # Converting unverified_user to a full UserData.
            num_tries = 0
            user_data = None
            while not user_data and num_tries < 2:
                # Double-check to ensure we don't create any duplicate ids!
                user_id = uid.new_user_id()
                user_data = user_models.UserData.insert_for(
                    user_id,
                    unverified_user.email,
                    username,
                    password,
                    birthdate=unverified_user.birthdate,
                    gender=gender)

                if not user_data:
                    self.render_json(
                        {
                            'errors': {
                                'username': "******"
                            }
                        },
                        camel_cased=True)
                    return
                elif user_data.username != username:
                    # Something went awry - insert_for may have returned
                    # an existing user due to an ID collision. Try again.
                    user_data = None
                num_tries += 1

            if not user_data:
                logging.error("Tried several times to create a new user " +
                              "unsuccessfully")
                self.render_json(
                    {
                        'errors': {
                            'username':
                            "******" +
                            "Please try again later."
                        }
                    },
                    camel_cased=True)
                return

            # Nickname is special since it requires updating external indices.
            user_data.update_nickname(values['nickname'])

            # TODO(benkomalo): move this into a transaction with the above creation
            unverified_user.delete()

        # TODO(benkomalo): give some kind of "congrats"/"welcome" notification
        Login.return_login_json(self, user_data, cont=user_data.profile_root)