예제 #1
0
    def _do_get(self, phrase):
        """
        Fetch all categories and items data for a particular user.

        :param phrase: decryption passphrase

        :returns: success result along with categories and items arrays
        """
        cat_array = []
        item_array = []
        cipher = aescipher.AESCipher(phrase)
        try:
            categories = api.category_getall(self.session, self.user)
            for category in categories:
                cat_array.append(category.extract(cipher, with_items=False))

            items = api.item_getall(self.session, self.user)
            for item in items:
                item_array.append(item.extract(cipher, with_category=False))
        except UnicodeDecodeError:
            raise bh.OppError("Unable to decrypt data!")
        except Exception:
            raise bh.OppError("Unable to fetch from the database!")

        return {
            'result': 'success',
            'categories': cat_array,
            'items': item_array
        }
예제 #2
0
    def _gen_pwd(self, words, options):
        """
        Wrapper function around xkcdpass password generation logic

        :param words: list of words to use for password generation
        :param options: additional password generation constraints

        :returns: generated multi-word password
        """
        try:
            numwords = options['numwords']
        except (TypeError, KeyError):
            numwords = 6
        try:
            delimiter = options['delimiter']
        except (TypeError, KeyError):
            delimiter = " "

        # Sanity validation
        if numwords < 1 or numwords > 20:
            msg = "Invalid password generation options!"
            desc = ("For sanity's sake, numwords must be a "
                    "positive number less than or equal to 20.")
            raise bh.OppError(msg, desc)

        try:
            return xp.generate_xkcdpassword(words,
                                            numwords=numwords,
                                            interactive=False,
                                            acrostic=False,
                                            delimiter=delimiter)
        except Exception:
            raise bh.OppError("Exception during password generation!", None,
                              500)
예제 #3
0
    def _get_words(self, options):
        """Locate a word file and parse it to get a list of words from which
        xkcdpass module will randomly choose a passphrase. The word file may
        be specified in the configuration file specific to a particular
        deployment. Otherwise the algorithm will try to locate a standard
        word file from well known locations.

        :param options: contains options for xkcdpass configuring the type of
        words to include.

        :returns: list of words
        """
        wordfile = CONFIG['wordfile'] or xp.locate_wordfile()

        try:
            min_length = options['min_length']
        except (TypeError, KeyError):
            min_length = 5
        try:
            max_length = options['max_length']
        except (TypeError, KeyError):
            max_length = 9
        try:
            valid_chars = options['valid_chars']
        except (TypeError, KeyError):
            valid_chars = "."

        # Sanity validation
        msg = "Invalid password generation options!"

        if min_length < 1 or min_length > 15:
            desc = ("For sanity's sake, minimum length must be "
                    "a positive number less than or equal to 15.")
            raise bh.OppError(msg, desc)

        if max_length < 5 or max_length > 20:
            desc = ("For sanity's sake, maximum length must"
                    " be a number between 5 and 20.")
            raise bh.OppError(msg, desc)

        if min_length > max_length:
            desc = "Minimum length cannot be larger than maximum length."
            raise bh.OppError(msg, desc)

        try:
            return xp.generate_wordlist(wordfile=wordfile,
                                        min_length=min_length,
                                        max_length=max_length,
                                        valid_chars=valid_chars)
        except Exception:
            raise bh.OppError("Exception during password generation!", None,
                              500)
예제 #4
0
    def make_item(self, row, cipher, password, item_id=None):
        """
        Extract various item data from the request and encrypt it

        :param row: item object parsed from JSON request
        :param cipher: encryption cipher
        :param password: if specified, ignore the `password` field in
        in the item object and instead use this auto-generated password.
        :param item_id: should be specified for item update operations,
        None otherwise.

        :returns Item ORM model for insertion into the database
        """
        # Extract various item data into a list
        name = self._parse_or_set_empty(row, 'name')
        url = self._parse_or_set_empty(row, 'url')
        account = self._parse_or_set_empty(row, 'account')
        username = self._parse_or_set_empty(row, 'username')
        password = password or self._parse_or_set_empty(row, 'password')
        blob = self._parse_or_set_empty(row, 'blob')
        category_id = self._parse_or_set_empty(row, 'category_id', True)

        try:
            return models.Item(id=item_id,
                               name=cipher.encrypt(name),
                               url=cipher.encrypt(url),
                               account=cipher.encrypt(account),
                               username=cipher.encrypt(username),
                               password=cipher.encrypt(password),
                               blob=cipher.encrypt(blob),
                               category_id=category_id,
                               user=self.user)
        except (AttributeError, TypeError):
            raise bh.OppError("Invalid item data in list!")
예제 #5
0
    def _do_put(self, phrase):
        """
        Create a list of items, given an array of item parameters, with
        option to specify auto-generation of common or unique passwords
        for each item.

        :param phrase: decryption passphrase

        :returns: success result along with array of newly created items
        """
        payload_dicts = [{
            'name': "items",
            'is_list': True,
            'required': True
        }, {
            'name': "auto_pass",
            'is_list': False,
            'required': False
        }, {
            'name': "unique",
            'is_list': False,
            'required': False
        }, {
            'name': "genopts",
            'is_list': False,
            'required': False
        }]
        item_list, auto_pass, unique, genopts = self._check_payload(
            payload_dicts)

        if auto_pass is True:
            # Retrieve words dictionary
            words = self._get_words(genopts)

            # Generate common password for all items
            if unique is not True:
                common_password = self._gen_pwd(words, genopts)

        cipher = aescipher.AESCipher(phrase)
        items = []
        for row in item_list:
            if auto_pass is True:
                if unique is True:
                    password = self._gen_pwd(words, genopts)
                else:
                    password = common_password
            else:
                password = None

            items.append(self.make_item(row, cipher, password))

        try:
            items = api.item_create(self.session, items)
            response = []
            for item in items:
                response.append(item.extract(cipher))
            return {'result': 'success', 'items': response}
        except Exception:
            raise bh.OppError("Unable to add new items to the database!")
예제 #6
0
    def _do_put(self, phrase):
        """
        Create a new user.

        :param phrase: not used

        :returns: success result
        """
        payload_dicts = [{
            'name': "username",
            'is_list': False,
            'required': True
        }, {
            'name': "password",
            'is_list': False,
            'required': True
        }, {
            'name': "phrase",
            'is_list': False
        }]
        payload_objects = self._check_payload(payload_dicts)

        u = self._validate(payload_objects[0], 'username')
        p = self._validate(payload_objects[1], 'password')
        phrase = self._validate(payload_objects[2], 'phrase')
        if len(phrase) < 6:
            raise bh.OppError("Passphrase must be at least 6 characters long!")

        try:
            cipher = aescipher.AESCipher(phrase)
            ok = cipher.encrypt("OK")
            user = api.user_get_by_username(self.session, u)
            if user:
                raise bh.OppError("User already exists!")
            hashed = utils.hashpw(p)
            user = models.User(username=u, password=hashed, phrase_check=ok)
            api.user_create(self.session, user)
            user = api.user_get_by_username(self.session, u)
            if user:
                return {'result': 'success'}
            else:
                raise bh.OppError("Unable to add user: '******'" % u)
        except bh.OppError as e:
            raise bh.OppError(e.error, e.desc, e.status, e.headers)
        except Exception:
            raise bh.OppError("Unable to add user: '******'" % u)
예제 #7
0
    def _do_get(self, phrase):
        """
        Fetch all user's items.

        :param phrase: decryption passphrase

        :returns: success result along with decrypted items array
        """
        response = []
        cipher = aescipher.AESCipher(phrase)
        try:
            items = api.item_getall(self.session, self.user)
            for item in items:
                response.append(item.extract(cipher))
        except UnicodeDecodeError:
            raise bh.OppError("Unable to decrypt data!")
        except Exception:
            raise bh.OppError("Unable to fetch items from the database!")
        return {'result': 'success', 'items': response}
예제 #8
0
    def _do_delete(self):
        """
        Delete a user.

        :returns: success result
        """
        payload_dicts = [{
            'name': "username",
            'is_list': False,
            'required': True
        }, {
            'name': "password",
            'is_list': False,
            'required': True
        }]
        payload_objects = self._check_payload(payload_dicts)

        u = self._validate(payload_objects[0], 'username')
        p = self._validate(payload_objects[1], 'password')

        try:
            user = api.user_get_by_username(self.session, u)
            if not user:
                raise bh.OppError("User does not exist!")
            if not utils.checkpw(p, user.password):
                raise bh.OppError("Incorrect password supplied!")

            api.category_delete_all(self.session, user, True)
            api.item_delete_all(self.session, user)
            api.user_delete(self.session, user)

            user = api.user_get_by_username(self.session, u)
            if user:
                bh.OppError("Unable to delete user: '******'" % u)
            else:
                return {'result': 'success'}
        except bh.OppError as e:
            raise bh.OppError(e.error, e.desc, e.status, e.headers)
        except Exception:
            raise bh.OppError("Unable to delate user: '******'" % u)
예제 #9
0
    def _validate(self, obj, name):
        """
        Validate the input object.

        :param obj: input object
        :param name: input object name

        :returns validated object or raises an error
        """
        obj = obj.strip()
        if not obj:
            raise bh.OppError("Input '%s' parameter is empty!" % name)

        return obj
예제 #10
0
    def _do_delete(self):
        """
        Delete a list of items, identified by id.

        :returns: success result
        """
        payload_dicts = [{'name': "ids", 'is_list': True, 'required': True}]
        payload_objects = self._check_payload(payload_dicts)
        id_list = payload_objects[0]

        try:
            api.item_delete_by_id(self.session, self.user, id_list)
            return {'result': "success"}
        except Exception:
            raise bh.OppError("Unable to delete items from the database!")
예제 #11
0
    def _do_post(self, phrase):
        """
        Update a user.

        :param phrase: not used

        :returns: success result
        """
        payload_dicts = [{
            'name': "username",
            'is_list': False,
            'required': True
        }, {
            'name': "password",
            'is_list': False,
            'required': True
        }, {
            'name': "new_username",
            'is_list': False,
            'required': False
        }, {
            'name': "new_password",
            'is_list': False,
            'required': False
        }]
        payload_objects = self._check_payload(payload_dicts)

        u = self._validate(payload_objects[0], 'username')
        p = self._validate(payload_objects[1], 'password')
        new_u = payload_objects[2].strip()
        new_p = payload_objects[3].strip()
        if not (new_u or new_p):
            raise bh.OppError("at least one of: "
                              "[--new_username, --new_password] is required!")

        try:
            user = api.user_get_by_username(self.session, u)
            if not user:
                raise bh.OppError("User does not exist!")
            if not utils.checkpw(p, user.password):
                raise bh.OppError("Incorrect password supplied!")

            if new_u:
                new_user = api.user_get_by_username(self.session, new_u)
                if new_user:
                    raise bh.OppError("Username: '******' already exists!" % new_u)

            if new_u:
                user.username = new_u
            if new_p:
                user.password = utils.hashpw(new_p)

            api.user_update(self.session, user)
            user = api.user_get_by_username(self.session, user.username)
            if user:
                return {'result': 'success'}
            else:
                raise bh.OppError("Unable to add user: '******'" % u)
        except bh.OppError as e:
            raise bh.OppError(e.error, e.desc, e.status, e.headers)
        except Exception:
            raise bh.OppError("Unable to update user: '******'" % u)
예제 #12
0
    def _do_post(self, phrase):
        """
        Update a list of items. Similar options to create except that
        item id is required for each item in the list.

        :param phrase: decryption passphrase

        :returns: success result
        """
        payload_dicts = [{
            'name': "items",
            'is_list': True,
            'required': True
        }, {
            'name': "auto_pass",
            'is_list': False,
            'required': False
        }, {
            'name': "unique",
            'is_list': False,
            'required': False
        }, {
            'name': "genopts",
            'is_list': False,
            'required': False
        }]
        item_list, auto_pass, unique, genopts = self._check_payload(
            payload_dicts)

        if auto_pass is True:
            # Retrieve words dictionary
            words = self._get_words(genopts)

            # Generate common password for all items if needed
            if unique is not True:
                common_password = self._gen_pwd(words, genopts)

        cipher = aescipher.AESCipher(phrase)
        items = []
        for row in item_list:
            # Make sure item id is parsed from request
            try:
                item_id = row['id']
            except KeyError:
                raise bh.OppError("Missing item id in list!")
            if not item_id:
                raise bh.OppError("Empty item id in list!")

            if auto_pass is True:
                if unique is True:
                    password = self._gen_pwd(words, genopts)
                else:
                    password = common_password
            else:
                password = None

            items.append(self.make_item(row, cipher, password, item_id))

        try:
            api.item_update(self.session, items)
            response = []
            for item in items:
                response.append(item.extract(cipher))
            return {'result': 'success', 'items': response}
        except Exception:
            raise bh.OppError("Unable to update items in the database!")