def test_auth_su_notification_edit(self):
        """
        Test that SUPERADMINS can edit the notification text.
        """
        # Create a SUPERUSER login.
        self.sessionToken = self.auth_client.performLogin(
            "Username:Password", "root:root")

        ret = self.auth_client.addPermission(Permission.SUPERUSER, "root",
                                             False, "")
        self.assertTrue(ret)
        # we got the permission

        su_auth_client = \
            env.setup_auth_client(self._test_workspace,
                                  session_token=self.sessionToken)

        su_config_client = \
            env.setup_config_client(self._test_workspace,
                                    session_token=self.sessionToken)

        user = su_auth_client.getLoggedInUser()
        self.assertEqual(user, "root")
        # we are root

        su_config_client.setNotificationBannerText(
            convert.to_b64('su notification'))
        self.assertEqual(
            convert.from_b64(su_config_client.getNotificationBannerText()),
            'su notification')
Exemplo n.º 2
0
    def setNotificationBannerText(self, notification_b64):
        """
        Sets the notification banner remove_products_except.
        Bevare: This method only works if the use is a SUPERUSER.
        """

        self.__require_supermission()

        notification = convert.from_b64(notification_b64)

        with DBSession(self.__session) as session:
            notificationQuery = session.query(Configuration) \
                .filter(
                    Configuration.config_key == 'notification_banner_text') \
                .one_or_none()

            if notificationQuery is None:
                conf = Configuration('notification_banner_text', notification)
                session.add(conf)
                session.flush()
            else:
                # update it
                notificationQuery.config_value = notification

            session.commit()
            session.close()
Exemplo n.º 3
0
    def test_get_product_data(self):
        """
        Test getting product configuration from server.
        """

        # First, test calling the API through a product endpoint and not the
        # global endpoint. Also retrieve product ID this way.
        pr_client = env.setup_product_client(self.test_workspace,
                                             product=self.product_name)
        self.assertIsNotNone(pr_client, "Couldn't set up client")

        # This returns a USERSPACE product data.
        pr_data = pr_client.getCurrentProduct()

        self.assertIsNotNone(pr_data,
                             "Couldn't retrieve product data properly")

        self.assertEqual(pr_data.endpoint, self.product_name,
                         "The product's endpoint is improper.")
        self.assertTrue(pr_data.id > 0, "Product didn't have valid ID")

        # The connected attribute of a product will be always True if
        # database status is OK.
        connected = pr_data.databaseStatus == DBStatus.OK
        self.assertEqual(pr_data.connected, connected)

        # Now get the SERVERSPACE (configuration) for the product.
        # TODO: These things usually should only work for superusers!
        pr_conf = self._pr_client.getProductConfiguration(pr_data.id)

        self.assertIsNotNone(pr_conf, "Product configuration must come.")
        self.assertEqual(
            pr_conf.endpoint, self.product_name,
            "Product endpoint reproted by server must be the "
            "used endpoint... something doesn't make sense here!")

        self.assertIsNotNone(
            pr_conf.connection, "Product configuration must "
            "send a database connection.")

        self.assertIsNone(
            pr_conf.connection.password_b64,
            "!SECURITY LEAK! Server should NEVER send the "
            "product's database password out!")

        self.assertIn(
            self.product_name, pr_conf.connection.database,
            "The product's database (name|file) should contain "
            "the product's endpoint -- in the test context.")

        name = convert.from_b64(pr_conf.displayedName_b64) \
            if pr_conf.displayedName_b64 else ''
        self.assertEqual(
            name,
            # libtest/codechecker.py uses the workspace's name.
            os.path.basename(self.test_workspace),
            "The displayed name must == the default value, as "
            "we didn't specify a custom displayed name.")
Exemplo n.º 4
0
def handle_list_products(args):

    # If the given output format is not 'table', redirect logger's output to
    # the stderr.
    stream = None
    if 'output_format' in args and args.output_format != 'table':
        stream = 'stderr'

    init_logger(args.verbose if 'verbose' in args else None, stream)

    protocol, host, port = split_server_url(args.server_url)
    client = setup_product_client(protocol, host, port)
    products = client.getProducts(None, None)

    if args.output_format == 'json':
        results = []
        for product in products:
            results.append({product.endpoint: product})
        print(CmdLineOutputEncoder().encode(results))
    else:  # plaintext, csv
        header = ['Database status', 'Endpoint', 'Name', 'Description']
        rows = []
        for product in products:
            name = convert.from_b64(product.displayedName_b64) \
                if product.displayedName_b64 else ''
            description = convert.from_b64(product.description_b64) \
                if product.description_b64 else ''

            if not product.accessible:
                db_status_msg = 'No access.'
            else:
                db_status = product.databaseStatus
                db_status_msg = database_status.db_status_msg.get(
                    db_status, 'Unknown database status')

            rows.append((db_status_msg,
                         product.endpoint, name, description))

        print(twodim_to_str(args.output_format, header, rows))
Exemplo n.º 5
0
    def test_get_source_file_content(self):
        """
        Test getting the source file content stored to the database.
        Test unicode support the stored file can be decoded properly
        compare results form the database to the original file.
        """
        runid = self._runid
        report_filter = ReportFilter(checkerName=['*'], filepath=['*.c*'])

        run_result_count = self._cc_client.getRunResultCount([runid],
                                                             report_filter,
                                                             None)
        self.assertTrue(run_result_count)

        run_results = get_all_run_results(self._cc_client,
                                          runid,
                                          [],
                                          report_filter)
        self.assertIsNotNone(run_results)

        for run_res in run_results:
            self.assertTrue(re.match(r'.*\.c(pp)?$', run_res.checkedFile))

            logging.debug('Getting the content of ' + run_res.checkedFile)

            file_data = self._cc_client.getSourceFileData(run_res.fileId,
                                                          True,
                                                          None)
            self.assertIsNotNone(file_data)

            file_content1 = file_data.fileContent
            self.assertIsNotNone(file_content1)

            with codecs.open(run_res.checkedFile, 'r', encoding='utf-8',
                             errors='replace') as source_file:
                file_content2 = source_file.read()

            self.assertEqual(file_content1, file_content2)

            file_data_b64 = self._cc_client.getSourceFileData(
                run_res.fileId, True, Encoding.BASE64)
            self.assertIsNotNone(file_data_b64)

            file_content1_b64 = convert.from_b64(file_data_b64.fileContent)
            self.assertIsNotNone(file_content1_b64)

            self.assertEqual(file_content1_b64, file_content2)

        logging.debug('got ' + str(len(run_results)) + ' files')

        self.assertEqual(run_result_count, len(run_results))
Exemplo n.º 6
0
    def test_get_source_file_content_latin1_encoding(self):
        """ Test if the source file was saved with latin1 encoding.
        Test if the source file can be read back from the
        database even if it was not saved with utf-8 encoding.
        """
        runid = self._runid
        report_filter = ReportFilter(checkerName=['*'],
                                     filepath=['*call_and_message.cpp*'])

        run_result_count = self._cc_client.getRunResultCount([runid],
                                                             report_filter,
                                                             None)
        self.assertTrue(run_result_count)

        run_results = get_all_run_results(self._cc_client,
                                          runid,
                                          [],
                                          report_filter)
        self.assertIsNotNone(run_results)
        self.assertIsNotNone(run_results)

        for run_res in run_results:

            print('Getting the content of ' + run_res.checkedFile)

            file_data = self._cc_client.getSourceFileData(run_res.fileId,
                                                          True,
                                                          None)
            self.assertIsNotNone(file_data)

            file_content1 = file_data.fileContent
            self.assertIsNotNone(file_content1)

            with codecs.open(run_res.checkedFile, 'r', encoding='utf-8',
                             errors='ignore') as source_file:
                file_content2 = source_file.read()
            self.assertEqual(file_content1, file_content2)

            file_data_b64 = self._cc_client.getSourceFileData(
                run_res.fileId, True, Encoding.BASE64)
            self.assertIsNotNone(file_data_b64)

            file_content1_b64 = convert.from_b64(file_data_b64.fileContent)
            self.assertIsNotNone(file_content1_b64)

            self.assertEqual(file_content1_b64, file_content2)

        print('got ' + str(len(run_results)) + ' files')

        self.assertEqual(run_result_count, len(run_results))
    def __check_lines_in_source_file_contents(self, lines_in_files_requested,
                                              encoding, expected):
        """
        Get line content information with the given encoding and check the
        results.
        """
        file_to_lines_map = self._cc_client.getLinesInSourceFileContents(
            lines_in_files_requested, encoding)
        for file_id in expected:
            for line in expected[file_id]:
                source_line = file_to_lines_map[file_id][line]
                if encoding == Encoding.BASE64:
                    source_line = convert.from_b64(source_line)

                self.assertEqual(source_line, expected[file_id][line])
    def test_noauth_notification_edit(self):
        """
        Test for editing the notification text on a non authenting server.
        """

        # A non-authenticated session should return an empty user.
        user = self.auth_client.getLoggedInUser()
        self.assertEqual(user, "")

        # Server without authentication should allow notification setting.
        self.config_client.setNotificationBannerText(
            convert.to_b64('noAuth notif'))
        self.assertEqual(
            convert.from_b64(self.config_client.getNotificationBannerText()),
            'noAuth notif')
    def test_unicode_string(self):
        """
        Test for non ascii strings. Needed because the used Thrift
        version won't eat them.
        """

        # A non-authenticated session should return an empty user.
        user = self.auth_client.getLoggedInUser()
        self.assertEqual(user, "")

        # Check if utf-8 encoded strings are okay.
        self.config_client.setNotificationBannerText(
            convert.to_b64('árvíztűrő tükörfúrógép'))
        self.assertEqual(
            convert.from_b64(self.config_client.getNotificationBannerText()),
            'árvíztűrő tükörfúrógép')
Exemplo n.º 10
0
    def editProduct(self, product_id, new_config):
        """
        Edit the given product's properties to the one specified by
        new_configuration.
        """
        with DBSession(self.__session) as session:
            product = session.query(Product).get(product_id)
            if product is None:
                msg = "Product with ID {0} does not exist!".format(product_id)
                LOG.error(msg)
                raise codechecker_api_shared.ttypes.RequestFailed(
                    codechecker_api_shared.ttypes.ErrorCode.DATABASE, msg)

            # Editing the metadata of the product, such as display name and
            # description is available for product admins.

            # Because this query doesn't come through a product endpoint,
            # __init__ sets the value in the extra_args to None.
            self.__permission_args['productID'] = product.id
            self.__require_permission([permissions.PRODUCT_ADMIN])

            LOG.info("User requested edit product '%s'", product.endpoint)

            dbc = new_config.connection
            if not dbc:
                msg = "Product's database configuration cannot be removed!"
                LOG.error(msg)
                raise codechecker_api_shared.ttypes.RequestFailed(
                    codechecker_api_shared.ttypes.ErrorCode.GENERAL, msg)

            if new_config.endpoint != product.endpoint:
                if not is_valid_product_endpoint(new_config.endpoint):
                    msg = "The endpoint to move the product to is invalid."
                    LOG.error(msg)
                    raise codechecker_api_shared.ttypes.RequestFailed(
                        codechecker_api_shared.ttypes.ErrorCode.GENERAL, msg)

                if self.__server.get_product(new_config.endpoint):
                    LOG.error(
                        "A product endpoint '/%s' is already"
                        "configured!", product.endpoint)
                    raise codechecker_api_shared.ttypes.RequestFailed(
                        codechecker_api_shared.ttypes.ErrorCode.GENERAL, msg)

                LOG.info("User renamed product '%s' to '%s'", product.endpoint,
                         new_config.endpoint)

            # Some values come encoded as Base64, decode these.
            displayed_name = convert.from_b64(new_config.displayedName_b64) \
                if new_config.displayedName_b64 \
                else new_config.endpoint
            description = convert.from_b64(new_config.description_b64) \
                if new_config.description_b64 else None

            if dbc.engine == 'sqlite' and not os.path.isabs(dbc.database):
                # Transform the database relative path to be under the
                # server's config directory.
                dbc.database = os.path.join(self.__server.config_directory,
                                            dbc.database)

            # Transform arguments into a database connection string.
            if dbc.engine == 'postgresql':
                dbuser = "******"
                if dbc.username_b64 and dbc.username_b64 != '':
                    dbuser = convert.from_b64(dbc.username_b64)

                old_connection_args = SQLServer.connection_string_to_args(
                    product.connection)
                if dbc.password_b64 and dbc.password_b64 != '':
                    dbpass = convert.from_b64(dbc.password_b64)
                elif 'dbpassword' in old_connection_args:
                    # The password was not changed. Retrieving from old
                    # configuration -- if the old configuration contained such!
                    dbpass = old_connection_args['dbpassword']
                else:
                    dbpass = None

                conn_str_args = {
                    'postgresql': True,
                    'sqlite': False,
                    'dbaddress': dbc.host,
                    'dbport': dbc.port,
                    'dbusername': dbuser,
                    'dbpassword': dbpass,
                    'dbname': dbc.database
                }
            elif dbc.engine == 'sqlite':
                conn_str_args = {'postgresql': False, 'sqlite': dbc.database}
            else:
                msg = "Database engine '{0}' unknown!".format(dbc.engine)
                LOG.error(msg)
                raise codechecker_api_shared.ttypes.RequestFailed(
                    codechecker_api_shared.ttypes.ErrorCode.GENERAL, msg)

            conn_str = SQLServer \
                .from_cmdline_args(conn_str_args, IDENTIFIER, None,
                                   False, None).get_connection_string()

            # If endpoint or database arguments change, the product
            # configuration has changed so severely, that it needs
            # to be reconnected.
            product_needs_reconnect = \
                product.endpoint != new_config.endpoint or \
                product.connection != conn_str
            old_endpoint = product.endpoint

            if product_needs_reconnect:
                # Changing values that alter connection-specific data
                # should only be available for superusers!
                self.__require_permission([permissions.SUPERUSER])

                # Test if the new database settings are correct or not.
                dummy_endpoint = new_config.endpoint + '_' + ''.join(
                    random.sample(new_config.endpoint,
                                  min(len(new_config.endpoint), 5)))
                dummy_prod = Product(endpoint=dummy_endpoint,
                                     conn_str=conn_str,
                                     name=displayed_name,
                                     description=description)

                LOG.debug("Attempting database connection with new "
                          "settings...")

                # Connect and create the database schema.
                self.__server.add_product(dummy_prod)
                LOG.debug("Product database successfully connected to.")

                connection_wrapper = self.__server.get_product(dummy_endpoint)
                if connection_wrapper.last_connection_failure:
                    msg = "The configured connection for '/{0}' failed: {1}" \
                        .format(new_config.endpoint,
                                connection_wrapper.last_connection_failure)
                    LOG.error(msg)

                    self.__server.remove_product(dummy_endpoint)

                    raise codechecker_api_shared.ttypes.RequestFailed(
                        codechecker_api_shared.ttypes.ErrorCode.IOERROR, msg)

                # The orm_prod object above is not bound to the database as it
                # was just created. We use the actual database-backed
                # configuration entry to handle connections, so a "reconnect"
                # is issued later.
                self.__server.remove_product(dummy_endpoint)

            # Update the settings in the database.
            product.endpoint = new_config.endpoint
            product.run_limit = new_config.runLimit
            product.is_review_status_change_disabled = \
                new_config.isReviewStatusChangeDisabled
            product.connection = conn_str
            product.display_name = displayed_name
            product.description = description

            session.commit()
            LOG.info("Product configuration edited and saved successfully.")

            if product_needs_reconnect:
                product = session.query(Product).get(product_id)
                LOG.info("Product change requires database reconnection...")

                LOG.debug("Disconnecting...")
                self.__server.remove_product(old_endpoint)

                LOG.debug("Connecting new settings...")
                self.__server.add_product(product)

                LOG.info("Product reconnected successfully.")

            return True
Exemplo n.º 11
0
    def addProduct(self, product):
        """
        Add the given product to the products configured by the server.
        """
        self.__require_permission([permissions.SUPERUSER])

        session = None
        LOG.info("User requested add product '%s'", product.endpoint)

        if not is_valid_product_endpoint(product.endpoint):
            msg = "The specified endpoint is invalid."
            LOG.error(msg)
            raise codechecker_api_shared.ttypes.RequestFailed(
                codechecker_api_shared.ttypes.ErrorCode.GENERAL, msg)

        dbc = product.connection
        if not dbc:
            msg = "Product cannot be added without a database configuration!"
            LOG.error(msg)
            raise codechecker_api_shared.ttypes.RequestFailed(
                codechecker_api_shared.ttypes.ErrorCode.GENERAL, msg)

        if self.__server.get_product(product.endpoint):
            msg = "A product endpoint '/{0}' is already configured!" \
                .format(product.endpoint)
            LOG.error(msg)
            raise codechecker_api_shared.ttypes.RequestFailed(
                codechecker_api_shared.ttypes.ErrorCode.GENERAL, msg)

        # Some values come encoded as Base64, decode these.
        displayed_name = convert.from_b64(product.displayedName_b64) \
            if product.displayedName_b64 else product.endpoint
        description = convert.from_b64(product.description_b64) \
            if product.description_b64 else None

        if dbc.engine == 'sqlite' and not os.path.isabs(dbc.database):
            # Transform the database relative path to be under the
            # server's config directory.
            dbc.database = os.path.join(self.__server.config_directory,
                                        dbc.database)

        # Transform arguments into a database connection string.
        if dbc.engine == 'postgresql':
            dbuser = "******"
            dbpass = ""
            if dbc.username_b64 and dbc.username_b64 != '':
                dbuser = convert.from_b64(dbc.username_b64)
            if dbc.password_b64 and dbc.password_b64 != '':
                dbpass = convert.from_b64(dbc.password_b64)

            conn_str_args = {
                'postgresql': True,
                'sqlite': False,
                'dbaddress': dbc.host,
                'dbport': dbc.port,
                'dbusername': dbuser,
                'dbpassword': dbpass,
                'dbname': dbc.database
            }
        elif dbc.engine == 'sqlite':
            conn_str_args = {'postgresql': False, 'sqlite': dbc.database}
        else:
            msg = "Database engine '{0}' unknown!".format(dbc.engine)
            LOG.error(msg)
            raise codechecker_api_shared.ttypes.RequestFailed(
                codechecker_api_shared.ttypes.ErrorCode.GENERAL, msg)

        conn_str = SQLServer \
            .from_cmdline_args(conn_str_args, IDENTIFIER, None, False, None) \
            .get_connection_string()

        is_rws_change_disabled = product.isReviewStatusChangeDisabled

        # Create the product's entity in the database.
        with DBSession(self.__session) as session:
            orm_prod = Product(
                endpoint=product.endpoint,
                conn_str=conn_str,
                name=displayed_name,
                description=description,
                run_limit=product.runLimit,
                is_review_status_change_disabled=is_rws_change_disabled)

            LOG.debug("Attempting database connection to new product...")

            # Connect and create the database schema.
            self.__server.add_product(orm_prod, init_db=True)
            connection_wrapper = self.__server.get_product(product.endpoint)
            if connection_wrapper.last_connection_failure:
                msg = "The configured connection for '/{0}' failed: {1}" \
                    .format(product.endpoint,
                            connection_wrapper.last_connection_failure)
                LOG.error(msg)

                self.__server.remove_product(product.endpoint)

                raise codechecker_api_shared.ttypes.RequestFailed(
                    codechecker_api_shared.ttypes.ErrorCode.IOERROR, msg)

            LOG.debug("Product database successfully connected to.")
            session.add(orm_prod)
            session.flush()

            # Create the default permissions for the product
            permissions.initialise_defaults('PRODUCT', {
                'config_db_session': session,
                'productID': orm_prod.id
            })
            session.commit()
            LOG.debug("Product configuration added to database successfully.")

            # The orm_prod object above is not bound to the database as it
            # was just created. We use the actual database-backed configuration
            # entry to handle connections, so a "reconnect" is issued here.
            self.__server.remove_product(product.endpoint)

            orm_prod = session.query(Product) \
                .filter(Product.endpoint == product.endpoint).one()
            self.__server.add_product(orm_prod)

            LOG.debug("Product database connected and ready to serve.")
            return True