Example #1
0
    def test_change_password_good_is_password_changed(self):
        from c2cgeoportal_geoportal.views.login import Login
        import crypt

        request = self._create_request_obj(
            params={"lang": "en"},
            POST={
                "login": "******",
                "oldPassword": "******",
                "newPassword": "******",
                "confirmNewPassword": "******",
            },
        )

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User

        user = DBSession.query(User).filter_by(username="******").one()
        assert user.is_password_changed is False
        assert user._password == crypt.crypt("__test_user1", user._password)
        login = Login(request)
        self.assertNotEqual(login.change_password(), None)
        user = DBSession.query(User).filter_by(username="******").one()
        assert user.is_password_changed is True
        assert user._password == crypt.crypt("1234", user._password)
    def test_web_client_functionalities(self):
        from tests.functional import create_dummy_request

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User

        request = create_dummy_request()
        request.static_url = lambda url: "http://example.com/dummy/static/url"
        request1 = create_dummy_request()
        request1.static_url = lambda url: "http://example.com/dummy/static/url"
        request1.user = DBSession.query(User).filter(User.username == "__test_user1").one()
        request2 = create_dummy_request()
        request2.static_url = lambda url: "http://example.com/dummy/static/url"
        request2.user = DBSession.query(User).filter(User.username == "__test_user2").one()

        fill_tech_user_functionality(
            "registered", (("__test_s", "registered"), ("__test_a", "r1"), ("__test_a", "r2"))
        )
        fill_tech_user_functionality(
            "anonymous", (("__test_s", "anonymous"), ("__test_a", "a1"), ("__test_a", "a2"))
        )
        settings = {
            "functionalities": {"available_in_templates": ["__test_s", "__test_a"]},
            "admin_interface": {"available_functionalities": [{"name": "__test_a"}, {"name": "__test_s"}]},
        }
        request.registry.settings.update(settings)
        request1.registry.settings.update(settings)
        request2.registry.settings.update(settings)
Example #3
0
def init_db(settings: Dict[str, Any]) -> None:
    """
    Initialize the SQLAlchemy Session.

    First test the connection, on wen environment it should be OK, with the command line we should get
    an exception ind initialise the connection.
    """

    try:
        from c2cgeoportal_commons.models import DBSession  # pylint: disable=import-outside-toplevel
        from c2cgeoportal_commons.models.main import Theme  # pylint: disable=import-outside-toplevel

        session = DBSession()
        session.query(Theme).count()
    except:  # pylint: disable=bare-except

        # Init db sessions

        class R:
            settings: Dict[str, Any] = {}

        class C:
            registry = R()

            def get_settings(self) -> Dict[str, Any]:
                return self.registry.settings

            def add_tween(self, *args: Any, **kwargs: Any) -> None:
                pass

        config_ = C()
        config_.registry.settings = settings

        c2cgeoportal_geoportal.init_db_sessions(settings, config_)
Example #4
0
    def setup_method(self, _):
        setup_module()

        import transaction
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import Role, Interface
        from c2cgeoportal_commons.models.static import User

        cleanup_db()

        self.metadata = None
        self.layer_ids = []

        DBSession.query(User).delete()
        DBSession.query(User).filter(User.username == "__test_user").delete()

        self.role = Role(name="__test_role")
        self.user = User(username="******",
                         password="******",
                         settings_role=self.role,
                         roles=[self.role])
        self.main = Interface(name="main")

        DBSession.add(self.user)
        DBSession.add(self.role)
        DBSession.add(self.main)

        create_default_ogcserver()

        transaction.commit()
Example #5
0
    def __call__(self, filename, options):
        messages = []

        try:
            self.env = bootstrap(filename, options=os.environ)

            try:
                from c2cgeoportal_commons.models import DBSession
                from c2cgeoportal_commons.models.main import Theme, LayerGroup, \
                    LayerWMS, LayerWMTS, FullTextSearch

                self._import(Theme, messages)
                self._import(LayerGroup, messages)
                self._import(LayerWMS, messages, self._import_layer_wms)
                self._import(LayerWMTS, messages, self._import_layer_wmts)

                for ln, in DBSession.query(
                        FullTextSearch.layer_name).distinct().all():
                    if ln is not None and ln != "":
                        messages.append(
                            Message(
                                None, ln, None, [], "", "",
                                ("fts", ln.encode("ascii", errors="replace"))))

                for ln, in DBSession.query(
                        FullTextSearch.actions).distinct().all():
                    if ln is not None and ln != "":
                        action = json.loads(ln)
                        messages.append(
                            Message(None, action['data'], None, [], "", "",
                                    ("fts", action['data'].encode(
                                        "ascii", errors="replace"))))
            except ProgrammingError as e:
                print(
                    colorize(
                        "ERROR! The database is probably not up to date "
                        "(should be ignored when happen during the upgrade)",
                        RED))
                print(colorize(e, RED))
                if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
                    raise
        except NoSuchTableError as e:
            print(
                colorize(
                    "ERROR! The schema didn't seem to exists "
                    "(should be ignored when happen during the deploy)", RED))
            print(colorize(e, RED))
            if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
                raise
        except OperationalError as e:
            print(
                colorize(
                    "ERROR! The database didn't seem to exists "
                    "(should be ignored when happen during the deploy)", RED))
            print(colorize(e, RED))
            if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
                raise

        return messages
    def teardown_method(self, _):
        testing.tearDown()

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import Shorturl
        import transaction
        DBSession.query(Shorturl).delete()
        transaction.commit()
Example #7
0
    def teardown_method(self, _):
        testing.tearDown()

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import Shorturl
        import transaction
        DBSession.query(Shorturl).delete()
        transaction.commit()
Example #8
0
    def teardown_method(self, _):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User

        DBSession.query(User).filter(User.username == "__test_user1").delete()
        transaction.commit()

        pyramid.security.remember = None
        pyramid.security.remember = self.old_remember
Example #9
0
 def get_ogcserver_byname(self, name):
     try:
         result = DBSession.query(OGCServer).filter(OGCServer.name == name).one()
         DBSession.expunge(result)
         return result
     except NoResultFound:  # pragma nocover
         log.error("OGSServer '{}' does not exists (existing: {}).".format(
             name, ",".join([t[0] for t in DBSession.query(OGCServer.name).all()])))
         raise
Example #10
0
 def get_ogcserver_byname(self, name):
     try:
         result = DBSession.query(OGCServer).filter(OGCServer.name == name).one()
         DBSession.expunge(result)
         return result
     except NoResultFound:  # pragma nocover
         log.error("OGSServer '{}' does not exists (existing: {}).".format(
             name, ",".join([t[0] for t in DBSession.query(OGCServer.name).all()])))
         raise
Example #11
0
    def teardown_method(self, _):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User

        DBSession.query(User).filter(User.username == "__test_user1").delete()
        transaction.commit()

        pyramid.security.remember = None
        pyramid.security.remember = self.old_remember
Example #12
0
    def test_json_extent(self):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import Role

        role = DBSession.query(Role).filter(Role.name == "__test_role1").one()
        assert role.bounds is None

        role = DBSession.query(Role).filter(Role.name == "__test_role2").one()
        assert role.bounds == (1, 2, 3, 4)
Example #13
0
 def lux_cancel(self):
     ref = self.request.matchdict.get("ref")
     job = DBSession.query(LuxPrintJob).get(ref)
     print_url = job.print_url
     DBSession.query(LuxPrintJob).filter(LuxPrintJob.id == ref).delete()
     return self._proxy_response(
         "print",
         "%s/cancel/%s" % (print_url, ref),
     )
Example #14
0
    def teardown_method(self, _):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import Role, LayerWMS, RestrictionArea, \
            Interface, OGCServer
        from c2cgeoportal_commons.models.static import User

        DBSession.query(User).filter(User.username == "__test_user1").delete()

        ra = DBSession.query(RestrictionArea).filter(
            RestrictionArea.name == "__test_ra1").one()
        ra.roles = []
        ra.layers = []
        DBSession.delete(ra)

        r = DBSession.query(Role).filter(Role.name == "__test_role1").one()
        DBSession.delete(r)

        for layer in DBSession.query(LayerWMS).filter(
                LayerWMS.name.in_(
                    ["testpoint_group_name",
                     "testpoint_protected_2_name"])).all():
            DBSession.delete(layer)
        DBSession.query(Interface).filter(Interface.name == "main").delete()
        DBSession.query(OGCServer).delete()

        transaction.commit()
    def teardown_method(self, _):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import Role, LayerWMS, RestrictionArea, \
            Interface, OGCServer
        from c2cgeoportal_commons.models.static import User

        DBSession.query(User).filter(User.username == "__test_user1").delete()

        ra = DBSession.query(RestrictionArea).filter(
            RestrictionArea.name == "__test_ra1"
        ).one()
        ra.roles = []
        ra.layers = []
        DBSession.delete(ra)

        r = DBSession.query(Role).filter(Role.name == "__test_role1").one()
        DBSession.delete(r)

        for layer in DBSession.query(LayerWMS).filter(
                LayerWMS.name.in_(["testpoint_group_name", "testpoint_protected_2_name"])
        ).all():
            DBSession.delete(layer)
        DBSession.query(Interface).filter(
            Interface.name == "main"
        ).delete()
        DBSession.query(OGCServer).delete()

        transaction.commit()
Example #16
0
    def teardown_method(self, _):
        import transaction
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import Role
        from c2cgeoportal_commons.models.static import User

        transaction.commit()

        DBSession.query(User).filter_by(username="******").delete()
        DBSession.query(Role).filter_by(name="__test_role").delete()
        transaction.commit()
    def teardown_method(self, _):
        import transaction
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import Role
        from c2cgeoportal_commons.models.static import User

        transaction.commit()

        DBSession.query(User).filter_by(username="******").delete()
        DBSession.query(Role).filter_by(name="__test_role").delete()
        transaction.commit()
    def test_web_client_functionalities(self):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User
        from tests.functional import create_dummy_request
        from c2cgeoportal_geoportal.views.entry import Entry

        request = create_dummy_request()
        request.static_url = lambda url: "http://example.com/dummy/static/url"
        request1 = create_dummy_request()
        request1.static_url = lambda url: "http://example.com/dummy/static/url"
        request1.user = DBSession.query(User).filter(
            User.username == "__test_user1").one()
        request2 = create_dummy_request()
        request2.static_url = lambda url: "http://example.com/dummy/static/url"
        request2.user = DBSession.query(User).filter(
            User.username == "__test_user2").one()

        settings = {
            "functionalities": {
                "anonymous": {
                    "__test_s": "anonymous",
                    "__test_a": ["a1", "a2"]
                },
                "registered": {
                    "__test_s": "registered",
                    "__test_a": ["r1", "r2"]
                },
                "available_in_templates": ["__test_s", "__test_a"],
            },
            "admin_interface": {
                "available_functionalities": ["__test_a", "__test_s"]
            },
        }
        functionality.FUNCTIONALITIES_TYPES = None
        request.registry.settings.update(settings)
        request1.registry.settings.update(settings)
        request2.registry.settings.update(settings)

        annon = Entry(request).get_cgxp_viewer_vars()
        u1 = Entry(request1).get_cgxp_viewer_vars()
        u2 = Entry(request2).get_cgxp_viewer_vars()
        self.assertEqual(annon["functionality"], {
            "__test_s": ["anonymous"],
            "__test_a": ["a1", "a2"]
        })
        self.assertEqual(u1["functionality"], {
            "__test_s": ["registered"],
            "__test_a": ["r1", "r2"]
        })
        self.assertEqual(u2["functionality"], {
            "__test_s": ["db"],
            "__test_a": ["db1", "db2"]
        })
Example #19
0
    def teardown_method(self, _):
        testing.tearDown()

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import TreeItem, Interface, OGCServer

        for item in DBSession.query(TreeItem).all():
            DBSession.delete(item)
        DBSession.query(OGCServer).delete()
        DBSession.query(Interface).filter(Interface.name == "main").delete()

        transaction.commit()
Example #20
0
 def _get_ogcserver_byname(self, name):  # pylint: disable=no-self-use
     try:
         result = DBSession.query(OGCServer).filter(
             OGCServer.name == name).one()
         DBSession.expunge(result)
         return result
     except NoResultFound:  # pragma nocover
         raise HTTPBadRequest(
             "OGSServer '{}' does not exists (existing: {}).".format(
                 name, ",".join([
                     t[0] for t in DBSession.query(OGCServer.name).all()
                 ])))
Example #21
0
def main():
    parser = argparse.ArgumentParser(
        description="Create and populate the database tables.")
    parser.add_argument('-i',
                        '--iniconfig',
                        default='production.ini',
                        help='project .ini config file')
    parser.add_argument(
        '-n',
        '--app-name',
        default="app",
        help='The application name (optional, default is "app")')

    options = parser.parse_args()

    # read the configuration
    env = {}
    env.update(os.environ)
    env["LOG_LEVEL"] = "INFO"
    env["GUNICORN_ACCESS_LOG_LEVEL"] = "INFO"
    env["C2CGEOPORTAL_LOG_LEVEL"] = "WARN"
    fileConfig(options.iniconfig, defaults=env)
    get_app(options.iniconfig, options.app_name, options=env)

    from c2cgeoportal_commons.models import DBSession
    from c2cgeoportal_commons.models.main import Interface, OGCServer, Theme, LayerGroup, LayerWMS

    session = DBSession()

    interfaces = session.query(Interface).all()
    ogc_server = session.query(OGCServer).filter(
        OGCServer.name == "source for image/png").one()

    layer_borders = LayerWMS("Borders", "borders")
    layer_borders.interfaces = interfaces
    layer_borders.ogc_server = ogc_server
    layer_density = LayerWMS("Density", "density")
    layer_density.interfaces = interfaces
    layer_density.ogc_server = ogc_server

    group = LayerGroup("Demo")
    group.children = [layer_borders, layer_density]

    theme = Theme("Demo")
    theme.children = [group]
    theme.interfaces = interfaces

    session.add(theme)

    transaction.commit()

    print("Successfully added the demo theme")
Example #22
0
    def teardown_method(self, _):
        testing.tearDown()

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import Interface, Metadata, TreeItem

        for t in DBSession.query(Metadata).all():
            DBSession.delete(t)
        for item in DBSession.query(TreeItem).all():
            DBSession.delete(item)
        DBSession.query(Interface).filter(Interface.name == "desktop").delete()

        transaction.commit()
    def teardown_method(self, _):
        testing.tearDown()

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import TreeItem, Interface, OGCServer

        for item in DBSession.query(TreeItem).all():
            DBSession.delete(item)
        DBSession.query(OGCServer).delete()
        DBSession.query(Interface).filter(
            Interface.name == "main"
        ).delete()

        transaction.commit()
    def teardown_method(self, _):
        testing.tearDown()

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import TreeItem, Interface, Metadata

        for t in DBSession.query(Metadata).all():
            DBSession.delete(t)
        for item in DBSession.query(TreeItem).all():
            DBSession.delete(item)
        DBSession.query(Interface).filter(
            Interface.name == "desktop"
        ).delete()

        transaction.commit()
    def teardown_method(self, _):
        testing.tearDown()

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, OGCServer, Theme

        for t in DBSession.query(Theme).filter(Theme.name == "__test_theme").all():
            DBSession.delete(t)
        for g in DBSession.query(LayerGroup).all():
            DBSession.delete(g)
        for layer in DBSession.query(LayerWMS).all():
            DBSession.delete(layer)
        DBSession.query(OGCServer).delete()

        transaction.commit()
Example #26
0
    def test_theme(self):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User
        from c2cgeoportal_geoportal.views.theme import Theme

        request = self._create_request_obj()
        theme_view = Theme(request)

        # unautenticated
        themes, errors = theme_view._themes()
        assert {e[:90] for e in errors} == set()
        assert len(themes) == 1
        groups = {g["name"] for g in themes[0]["children"]}
        assert groups == {"__test_layer_group"}
        layers = {l["name"] for l in themes[0]["children"][0]["children"]}
        assert layers == {"__test_public_layer"}

        # authenticated
        request.params = {}
        request.user = DBSession.query(User).filter_by(
            username="******").one()
        themes, errors = theme_view._themes()
        assert {e[:90] for e in errors} == set()
        assert len(themes) == 1
        groups = {g["name"] for g in themes[0]["children"]}
        assert groups == {"__test_layer_group"}
        layers = {l["name"] for l in themes[0]["children"][0]["children"]}
        assert layers == {
            "__test_private_layer_edit", "__test_public_layer",
            "__test_private_layer"
        }
Example #27
0
    def _import_layer_wmts(self, layer, messages):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import OGCServer

        layers = [d.value for d in layer.metadatas if d.name == "queryLayers"]
        if len(layers) == 0:
            layers = [d.value for d in layer.metadatas if d.name == "wmsLayer"]
        server = [d.value for d in layer.metadatas if d.name == "ogcServer"]
        if len(server) >= 1 and len(layers) >= 1:
            for wms_layer in layers:
                try:
                    db_server = DBSession.query(OGCServer).filter(OGCServer.name == server[0]).one()
                    self._import_layer_attributes(
                        db_server.url_wfs or db_server.url, wms_layer,
                        layer.item_type, layer.name, messages
                    )
                except NoResultFound:
                    print(colorize(
                        "ERROR! the OGC server '{}' from the WMTS layer '{}' does not exist.".format(
                            server[0], layer.name
                        ),
                        RED
                    ))
                    if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
                        raise
Example #28
0
    def test_group_update(self):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import LayerGroup
        layer_group_3 = DBSession.query(LayerGroup).filter(LayerGroup.name == "__test_layer_group_3").one()
        layer_group_3.children = layer_group_3.children[:-1]
        transaction.commit()

        entry = self._create_entry_obj(params={
            "version": "2",
            "group": "__test_layer_group_3",
            "catalogue": "true",
        })
        themes = entry.themes()
        self.assertEqual(self._get_filtered_errors(themes), set())
        self.assertEqual(
            self._only_name(themes["group"]),
            {
                "name": "__test_layer_group_3",
                # order is important
                "children": [{
                    "name": "__test_layer_wmts"
                }, {
                    "name": "__test_layer_internal_wms"
                }]
            }
        )
Example #29
0
    def test_create_log(self):
        from datetime import datetime

        from geojson.feature import FeatureCollection

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import Metadata
        from c2cgeoportal_commons.models.static import User
        from c2cgeoportal_geoportal.views.layers import Layers

        self.assertEqual(
            DBSession.query(User.username).all(), [("__test_user", )])

        metadatas = [
            Metadata("lastUpdateDateColumn", "last_update_date"),
            Metadata("lastUpdateUserColumn", "last_update_user"),
        ]
        layer_id = self._create_layer(metadatas=metadatas)
        request = self._get_request(layer_id, username="******")
        request.method = "POST"
        request.body = '{"type": "FeatureCollection", "features": [{"type": "Feature", "properties": {"name": "foo", "child": "c1é"}, "geometry": {"type": "Point", "coordinates": [5, 45]}}]}'  # noqa
        layers = Layers(request)
        collection = layers.create()
        self.assertEqual(request.response.status_int, 201)
        self.assertTrue(isinstance(collection, FeatureCollection))
        self.assertEqual(len(collection.features), 1)
        properties = collection.features[0]

        self.assertEqual(request.user.username, "__test_user")

        self.assertEqual(properties.last_update_user, request.user.id)
        self.assertIsInstance(properties.last_update_date, datetime)
    def _import_layer_wmts(self, layer, messages):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import OGCServer

        layers = [d.value for d in layer.metadatas if d.name == "queryLayers"]
        if len(layers) == 0:
            layers = [d.value for d in layer.metadatas if d.name == "wmsLayer"]
        server = [d.value for d in layer.metadatas if d.name == "ogcServer"]
        if len(server) >= 1 and len(layers) >= 1:
            for wms_layer in layers:
                try:
                    db_server = DBSession.query(OGCServer).filter(OGCServer.name == server[0]).one()
                    self._import_layer_attributes(
                        db_server.url_wfs or db_server.url, wms_layer,
                        layer.item_type, layer.name, messages
                    )
                except NoResultFound:
                    print(colorize(
                        "ERROR! the OGC server '{}' from the WMTS layer '{}' does not exist.".format(
                            server[0], layer.name
                        ),
                        RED
                    ))
                    if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
                        raise
Example #31
0
    def invalidate_authorization_code(
        self,
        client_id: str,
        code: str,
        request: oauthlib.common.Request,
        *args: Any,
        **kwargs: Any,
    ) -> None:
        """
        Invalidate an authorization code after use.

        Arguments:

            client_id: Unicode client identifier
            code: The authorization code grant (request.code).
            request: The HTTP Request

        Method is used by:
            - Authorization Code Grant
        """
        del args, kwargs

        LOG.debug("invalidate_authorization_code %s", client_id)

        from c2cgeoportal_commons.models import DBSession, static  # pylint: disable=import-outside-toplevel

        DBSession.delete(
            DBSession.query(static.OAuth2AuthorizationCode).join(
                static.OAuth2AuthorizationCode.client).filter(
                    static.OAuth2AuthorizationCode.code == code).filter(
                        static.OAuth2Client.client_id == client_id).filter(
                            static.OAuth2AuthorizationCode.user_id ==
                            request.user.id).one())
Example #32
0
    def validate_client_id(
        self,
        client_id: str,
        request: oauthlib.common.Request,
        *args: Any,
        **kwargs: Any,
    ) -> bool:
        """
        Ensure client_id belong to a valid and active client.

        Note, while not strictly necessary it can often be very convenient
        to set request.client to the client object associated with the
        given client_id.

        Arguments:

            client_id: Unicode client identifier
            request: oauthlib.common.Request

        Method is used by:
            - Authorization Code Grant
            - Implicit Grant
        """
        del args, kwargs

        LOG.debug("validate_client_id")

        from c2cgeoportal_commons.models import DBSession, static  # pylint: disable=import-outside-toplevel

        client = (DBSession.query(static.OAuth2Client).filter(
            static.OAuth2Client.client_id == client_id).one_or_none())
        if client is not None:
            request.client = client
        return client is not None
Example #33
0
    def get_user_from_request(request, username=None):
        """ Return the User object for the request.

        Return ``None`` if:
        * user is anonymous
        * it does not exist in the database
        * the referer is invalid
        """
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User

        try:
            if request.method == 'GET' and "auth" in request.params:
                auth_enc = request.params.get("auth")

                if auth_enc is not None:
                    urllogin = request.registry.settings.get("urllogin", {})
                    aeskey = urllogin.get("aes_key")
                    if aeskey is None:  # pragma: nocover
                        raise Exception("urllogin is not configured")
                    now = int(time.time())
                    data = binascii.unhexlify(auth_enc.encode('ascii'))
                    nonce = data[0:16]
                    tag = data[16:32]
                    ciphertext = data[32:]
                    cipher = AES.new(aeskey.encode("ascii"), AES.MODE_EAX,
                                     nonce)
                    auth = json.loads(
                        cipher.decrypt_and_verify(ciphertext,
                                                  tag).decode("utf-8"))

                    if "t" in auth and "u" in auth and "p" in auth:
                        timestamp = int(auth["t"])
                        if now < timestamp and request.registry.validate_user(
                                request, auth["u"], auth["p"]):
                            headers = pyramid.security.remember(
                                request, auth["u"])
                            request.response.headerlist.extend(headers)
        except Exception as e:
            LOG.error("URL login error: %s.", e, exc_info=True)

        if not hasattr(request, "is_valid_referer"):
            request.is_valid_referer = is_valid_referer(request, settings)
        if not request.is_valid_referer:
            LOG.debug("Invalid referer for %s: %s", request.path_qs,
                      repr(request.referer))
            return None

        if not hasattr(request, "user_"):
            request.user_ = None
            if username is None:
                username = request.authenticated_userid
            if username is not None:
                # We know we will need the role object of the
                # user so we use joined loading
                request.user_ = DBSession.query(User) \
                    .filter_by(username=username) \
                    .first()

        return request.user_
Example #34
0
    def test_group_update(self):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import LayerGroup
        layer_group_3 = DBSession.query(LayerGroup).filter(
            LayerGroup.name == "__test_layer_group_3").one()
        layer_group_3.children = layer_group_3.children[:-1]
        transaction.commit()

        entry = self._create_entry_obj(params={
            "group": "__test_layer_group_3",
        })
        themes = entry.themes()
        self.assertEqual(self._get_filtered_errors(themes), set())
        self.assertEqual(
            self._only_name(themes["group"]),
            {
                "name":
                "__test_layer_group_3",
                # order is important
                "children": [{
                    "name": "__test_layer_wmts"
                }, {
                    "name": "__test_layer_internal_wms"
                }]
            })
Example #35
0
    def _import_layer_wmts(self, layer: "main.Layer",
                           messages: List[str]) -> None:
        from c2cgeoportal_commons.models import DBSession  # pylint: disable=import-outside-toplevel
        from c2cgeoportal_commons.models.main import OGCServer  # pylint: disable=import-outside-toplevel

        layers = [d.value for d in layer.metadatas if d.name == "queryLayers"]
        if not layers:
            layers = [d.value for d in layer.metadatas if d.name == "wmsLayer"]
        server = [d.value for d in layer.metadatas if d.name == "ogcServer"]
        if server and layers:
            layers = [layer for ls in layers for layer in ls.split(",")]
            for wms_layer in layers:
                try:
                    db_server = DBSession.query(OGCServer).filter(
                        OGCServer.name == server[0]).one()
                    if db_server.wfs_support:
                        self._import_layer_attributes(
                            db_server.url_wfs or db_server.url,
                            wms_layer,
                            layer.item_type,
                            layer.name,
                            messages,
                        )
                except NoResultFound:
                    print(
                        colorize(
                            "ERROR! the OGC server '{}' from the WMTS layer '{}' does not exist."
                            .format(server[0], layer.name),
                            Color.RED,
                        ))
                    if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
                        raise
Example #36
0
    def add_association_proxy_xsd(self, tb: str, column_property: ColumnProperty) -> None:
        from c2cgeoportal_commons.models import DBSession  # pylint: disable=import-outside-toplevel

        column = column_property.columns[0]
        proxy = column.info["association_proxy"]
        attribute = column_property.class_attribute
        cls = attribute.parent.entity
        association_proxy = getattr(cls, proxy)
        relationship_property = class_mapper(cls).get_property(association_proxy.target)
        target_cls = relationship_property.argument
        query = DBSession.query(getattr(target_cls, association_proxy.value_attr))
        if association_proxy.order_by is not None:
            query = query.order_by(getattr(target_cls, association_proxy.order_by))
        attrs = {}
        if association_proxy.nullable:
            attrs["minOccurs"] = "0"
            attrs["nillable"] = "true"
        attrs["name"] = proxy
        with tag(tb, "xsd:element", attrs) as tb2:
            with tag(tb2, "xsd:simpleType") as tb3:
                with tag(tb3, "xsd:restriction", {"base": "xsd:string"}) as tb4:
                    for (value,) in query:
                        with tag(tb4, "xsd:enumeration", {"value": value}):
                            pass
            self.element_callback(tb4, column)
Example #37
0
    def test_reset_password(self):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User
        from c2cgeoportal_geoportal.views.entry import Entry

        request = self._create_request_obj(POST={
            "login": "******",
        })
        entry = Entry(request)
        _, username, password, _ = entry._loginresetpassword()

        request = self._create_request_obj(POST={
            "login": username,
            "password": password,
        })
        response = Entry(request).login()
        assert response.status_int == 200
        assert json.loads(response.body.decode("utf-8")) == {
            "success": True,
            "username": "******",
            'email': '*****@*****.**',
            "is_password_changed": False,
            "roles": [{
                "name": "__test_role1",
                "id": self.role1_id,
            }],
            "functionalities": {},
        }

        user = DBSession.query(User).filter(
            User.username == '__test_user1').first()
        self.assertIsNone(user.temp_password)
        self.assertIsNotNone(user.password)
        self.assertNotEqual(len(user.password), 0)
Example #38
0
    def test_wrong_loginresetpassword(self):
        from c2cgeoportal_geoportal.views.login import Login
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User

        user = DBSession.query(User).filter_by(username="******").one()
        _2fa_totp_secret = pyotp.random_base32()
        totp = pyotp.TOTP(_2fa_totp_secret)
        user.tech_data["2fa_totp_secret"] = _2fa_totp_secret
        user.is_password_changed = True

        request = self._create_request_obj(POST={"otp": totp.now()})
        with pytest.raises(pyramid.httpexceptions.HTTPBadRequest):
            Login(request).loginresetpassword()

        request = self._create_request_obj(POST={"login": "******"})
        with pytest.raises(pyramid.httpexceptions.HTTPBadRequest):
            Login(request).loginresetpassword()

        request = self._create_request_obj(POST={
            "login": "******",
            "otp": totp.now()
        })
        with pytest.raises(pyramid.httpexceptions.HTTPUnauthorized):
            Login(request).loginresetpassword()

        request = self._create_request_obj(POST={
            "login": "******",
            "otp": "wrong"
        })
        with pytest.raises(pyramid.httpexceptions.HTTPUnauthorized):
            Login(request).loginresetpassword()
Example #39
0
 def test_private(self):
     from c2cgeoportal_commons.models import DBSession
     from c2cgeoportal_commons.models.static import User
     entry = self._create_entry_obj(params={"version": "2"},
                                    user=DBSession.query(User).filter_by(
                                        username=u"__test_user").one())
     themes = entry.themes()
     self.assertEquals(self._get_filtered_errors(themes), set())
     self.assertEquals([self._only_name(t) for t in themes["themes"]], [{
         "name":
         u"__test_theme",
         "children": [{
             "name":
             u"__test_layer_group",
             "children": [{
                 "name": u"__test_layer_wms"
             }, {
                 "name": u"__test_layer_wms_private"
             }, {
                 "name": u"__test_layer_wmts"
             }, {
                 "name": u"__test_layer_wmts_private"
             }]
         }]
     }])
 def test_private(self):
     from c2cgeoportal_commons.models import DBSession
     from c2cgeoportal_commons.models.static import User
     entry = self._create_entry_obj(params={
         "version": "2"
     }, user=DBSession.query(User).filter_by(username=u"__test_user").one())
     themes = entry.themes()
     self.assertEquals(themes["errors"], [])
     self.assertEquals(
         [self._only_name(t) for t in themes["themes"]],
         [{
             "name": u"__test_theme",
             "children": [{
                 "name": u"__test_layer_group",
                 "children": [{
                     "name": u"__test_layer_wms"
                 }, {
                     "name": u"__test_layer_wms_private"
                 }, {
                     "name": u"__test_layer_wmts"
                 }, {
                     "name": u"__test_layer_wmts_private"
                 }]
             }]
         }]
     )
    def _create_getcap_request(username=None):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User

        request = create_dummy_request()
        request.user = None if username is None else \
            DBSession.query(User).filter_by(username=username).one()
        return request
def get_private_layers(ogc_server_ids):
    q = DBSession.query(LayerWMS) \
        .filter(LayerWMS.public.is_(False)) \
        .join(LayerWMS.ogc_server) \
        .filter(OGCServer.id.in_(ogc_server_ids))
    results = q.all()
    DBSession.expunge_all()
    return {r.id: r for r in results}
def main():
    parser = argparse.ArgumentParser(
        description="Create and populate the database tables."
    )
    parser.add_argument(
        '-i', '--iniconfig',
        default='geoportal/production.ini',
        help='project .ini config file'
    )
    parser.add_argument(
        '-n', '--app-name',
        default="app",
        help='The application name (optional, default is "app")'
    )

    options = parser.parse_args()

    # read the configuration
    fileConfig(options.iniconfig, defaults=os.environ)
    get_app(options.iniconfig, options.app_name, options=os.environ)

    from c2cgeoportal_commons.models import DBSession
    from c2cgeoportal_commons.models.main import Interface, OGCServer, Theme, LayerGroup, LayerWMS

    session = DBSession()

    interfaces = session.query(Interface).all()
    ogc_server = session.query(OGCServer).filter(OGCServer.name == "source for image/png").one()

    layer_borders = LayerWMS("Borders", "borders")
    layer_borders.interfaces = interfaces
    layer_borders.ogc_server = ogc_server
    layer_density = LayerWMS("Density", "density")
    layer_density.interfaces = interfaces
    layer_density.ogc_server = ogc_server

    group = LayerGroup("Demo")
    group.children = [layer_borders, layer_density]

    theme = Theme("Demo")
    theme.children = [group]
    theme.interfaces = interfaces

    session.add(theme)

    transaction.commit()
Example #44
0
def default_user_validator(request, username, password):
    """
    Validate the username/password. This is c2cgeoportal's
    default user validator.
    Return none if we are anonymous, the string to remember otherwise.
    """
    from c2cgeoportal_commons.models import DBSession
    from c2cgeoportal_commons.models.static import User
    user = DBSession.query(User).filter_by(username=username).first()
    return username if user and user.validate_password(password) else None
Example #45
0
def _get_layers_query(role_id, what):
    from c2cgeoportal_commons.models import DBSession
    from c2cgeoportal_commons.models.main import Layer, RestrictionArea, Role

    q = DBSession.query(what)
    q = q.join(Layer.restrictionareas)
    q = q.join(RestrictionArea.roles)
    q = q.filter(Role.id == role_id)

    return q
    def test_web_client_functionalities(self):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User
        from tests.functional import create_dummy_request
        from c2cgeoportal_geoportal.views.entry import Entry

        request = create_dummy_request()
        request.static_url = lambda url: "http://example.com/dummy/static/url"
        request1 = create_dummy_request()
        request1.static_url = lambda url: "http://example.com/dummy/static/url"
        request1.user = DBSession.query(User).filter(User.username == "__test_user1").one()
        request2 = create_dummy_request()
        request2.static_url = lambda url: "http://example.com/dummy/static/url"
        request2.user = DBSession.query(User).filter(User.username == "__test_user2").one()

        settings = {
            "functionalities": {
                "anonymous": {
                    "__test_s": "anonymous",
                    "__test_a": ["a1", "a2"]
                },
                "registered": {
                    "__test_s": "registered",
                    "__test_a": ["r1", "r2"]
                },
                "available_in_templates": ["__test_s", "__test_a"],
            },
            "admin_interface": {
                "available_functionalities": ["__test_a", "__test_s"]
            },
        }
        functionality.FUNCTIONALITIES_TYPES = None
        request.registry.settings.update(settings)
        request1.registry.settings.update(settings)
        request2.registry.settings.update(settings)

        annon = Entry(request).get_cgxp_viewer_vars()
        u1 = Entry(request1).get_cgxp_viewer_vars()
        u2 = Entry(request2).get_cgxp_viewer_vars()
        self.assertEqual(annon["functionality"], {"__test_s": ["anonymous"], "__test_a": ["a1", "a2"]})
        self.assertEqual(u1["functionality"], {"__test_s": ["registered"], "__test_a": ["r1", "r2"]})
        self.assertEqual(u2["functionality"], {"__test_s": ["db"], "__test_a": ["db1", "db2"]})
Example #47
0
    def get_user_from_request(request, username=None):
        """ Return the User object for the request.

        Return ``None`` if:
        * user is anonymous
        * it does not exist in the database
        * the referer is invalid
        """
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User

        try:
            if "auth" in request.params:
                auth_enc = request.params.get("auth")

                if auth_enc is not None:
                    urllogin = request.registry.settings.get("urllogin", {})
                    aeskey = urllogin.get("aes_key")
                    if aeskey is None:  # pragma: nocover
                        raise Exception("urllogin is not configured")
                    now = int(time.time())
                    cipher = AES.new(aeskey)
                    auth = json.loads(cipher.decrypt(binascii.unhexlify(auth_enc)))

                    if "t" in auth and "u" in auth and "p" in auth:
                        timestamp = int(auth["t"])
                        if now < timestamp and request.registry.validate_user(
                            request, auth["u"], auth["p"]
                        ):
                            headers = pyramid.security.remember(request, auth["u"])
                            request.response.headerlist.extend(headers)
        except Exception as e:
            log.error("URL login error: {}".format(e))

        if not hasattr(request, "is_valid_referer"):
            request.is_valid_referer = is_valid_referer(request, settings)
        if not request.is_valid_referer:
            log.warning(
                "Invalid referer for %s: %s", request.path_qs, repr(request.referer)
            )
            return None

        if not hasattr(request, "user_"):
            request.user_ = None
            if username is None:
                username = request.authenticated_userid
            if username is not None:
                # We know we will need the role object of the
                # user so we use joined loading
                request.user_ = DBSession.query(User) \
                    .filter_by(username=username) \
                    .first()

        return request.user_
Example #48
0
    def _import(object_type, messages, callback=None):
        from c2cgeoportal_commons.models import DBSession

        items = DBSession.query(object_type)
        for item in items:
            messages.append(Message(
                None, item.name, None, [], "", "",
                (item.item_type, item.name.encode("ascii", errors="replace"))
            ))

            if callback is not None:
                callback(item, messages)
Example #49
0
 def __set__(self, obj, val):
     from c2cgeoportal_commons.models import DBSession
     o = getattr(obj, self.target)
     # if the obj as no child object or if the child object
     # does not correspond to the new value then we need to
     # read a new child object from the database
     if not o or getattr(o, self.value_attr) != val:
         relationship_property = class_mapper(obj.__class__) \
             .get_property(self.target)
         child_cls = relationship_property.argument
         o = DBSession.query(child_cls).filter(
             getattr(child_cls, self.value_attr) == val).first()
         setattr(obj, self.target, o)
Example #50
0
    def __call__(self, filename, options):
        messages = []

        try:
            self.env = bootstrap(filename)

            try:
                from c2cgeoportal_commons.models import DBSession
                from c2cgeoportal_commons.models.main import Theme, LayerGroup, \
                    LayerWMS, LayerWMTS, FullTextSearch

                self._import(Theme, messages)
                self._import(LayerGroup, messages)
                self._import(LayerWMS, messages, self._import_layer_wms)
                self._import(LayerWMTS, messages, self._import_layer_wmts)

                for ln, in DBSession.query(FullTextSearch.layer_name).distinct().all():
                    if ln is not None and ln != "":
                        messages.append(Message(
                            None, ln, None, [], "", "",
                            ("fts", ln.encode("ascii", errors="replace"))
                        ))
            except ProgrammingError as e:
                print(colorize(
                    "ERROR! The database is probably not up to date "
                    "(should be ignored when happen during the upgrade)",
                    RED
                ))
                print(colorize(e, RED))
                if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
                    raise
        except NoSuchTableError as e:
            print(colorize(
                "ERROR! The schema didn't seem to exists "
                "(should be ignored when happen during the deploy)",
                RED
            ))
            print(colorize(e, RED))
            if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
                raise
        except OperationalError as e:
            print(colorize(
                "ERROR! The database didn't seem to exists "
                "(should be ignored when happen during the deploy)",
                RED
            ))
            print(colorize(e, RED))
            if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") != "TRUE":
                raise

        return messages
Example #51
0
    def get(self):
        ref = self.request.matchdict["ref"]
        short_urls = DBSession.query(Shorturl).filter(Shorturl.ref == ref).all()

        if len(short_urls) != 1:
            raise HTTPNotFound("Ref '{0!s}' not found".format(ref))

        short_urls[0].nb_hits += 1
        short_urls[0].last_hit = datetime.now()

        set_common_headers(
            self.request, "shortener", NO_CACHE
        )
        return HTTPFound(location=short_urls[0].url)
    def clean():
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User
        from c2cgeoportal_commons.models.main import TreeItem, Interface, Role, RestrictionArea, OGCServer

        for o in DBSession.query(RestrictionArea).all():
            o.roles = []
            o.layers = []
            DBSession.delete(o)
        for item in DBSession.query(TreeItem).all():
            DBSession.delete(item)
        DBSession.query(OGCServer).delete()
        DBSession.query(Interface).filter(
            Interface.name == "main"
        ).delete()
        DBSession.query(User).filter(
            User.username == "__test_user"
        ).delete()
        DBSession.query(Role).filter(
            Role.name == "__test_role"
        ).delete()

        transaction.commit()
    def difference(self):
        body = loads(self.request.body)
        if "geometries" not in body or \
                not isinstance(body["geometries"], list) or \
                len(body["geometries"]) != 2:  # pragma: no cover
            raise HTTPBadRequest("""Wrong body, it should be like that:
            {
                "geometries": [geomA, geomB]
            }
            """)

        return to_shape(DBSession.query(func.ST_Difference(
            from_shape(asShape(body["geometries"][0])),
            from_shape(asShape(body["geometries"][1]))
        )).scalar())
Example #54
0
    def check(request):
        url = request.route_url("themes")
        session = requests.session()
        for interface, in DBSession.query(Interface.name).all():
            params = {}
            params.update(default_params)
            params.update(interfaces_settings.get(interface, {}).get("params", {}))
            params["interface"] = interface

            interface_url_headers = build_url("checker_themes " + interface, url, request)

            response = session.get(params=params, **interface_url_headers)
            response.raise_for_status()

            result = response.json()
            if len(result["errors"]) != 0:
                raise Exception("Interface '{}': Theme with error\n{}".format(
                    interface, "\n".join(result["errors"])))
    def _create_dummy_request(username=None, params=None):
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.static import User

        request = create_dummy_request({
            "fulltextsearch": {
                "languages": {
                    "fr": "french",
                    "en": "english",
                    "de": "german",
                }
            }
        }, params=params)
        request.response = Response()
        request.user = None
        if username:
            request.user = DBSession.query(User) \
                .filter_by(username=username).one()
        return request
Example #56
0
def get_ogc_server_wfs_url_ids(request):
    from c2cgeoportal_commons.models import DBSession
    from c2cgeoportal_commons.models.main import OGCServer
    from c2cgeoportal_geoportal.lib.cacheversion import VersionCache
    global ogc_server_wfs_url_ids
    if ogc_server_wfs_url_ids is None:
        ogc_server_wfs_url_ids = VersionCache()

    errors = set()
    servers = ogc_server_wfs_url_ids.get()
    if servers is None:
        servers = dict()
        ogc_server_wfs_url_ids.set(servers)
        for ogc_server in DBSession.query(OGCServer).all():
            url = get_url2(ogc_server.name, ogc_server.url_wfs or ogc_server.url, request, errors)
            if servers.get(url) is None:
                servers[url] = []
            servers.get(url).append(ogc_server.id)
    return servers
    def setup_method(self, _):
        # Always see the diff
        # https://docs.python.org/2/library/unittest.html#unittest.TestCase.maxDiff
        self.maxDiff = None

        cleanup_db()

        self._tables = []

        import transaction
        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import Role, Interface, TreeItem, Theme, \
            LayerGroup, OGCSERVER_AUTH_NOAUTH
        from c2cgeoportal_commons.models.static import User

        for treeitem in DBSession.query(TreeItem).all():
            DBSession.delete(treeitem)

        self.role = Role(name="__test_role")
        self.user = User(
            username="******",
            password="******",
            role=self.role
        )
        self.main = Interface(name="main")

        self.ogc_server, external_ogc_server = create_default_ogcserver()
        self.ogc_server.auth = OGCSERVER_AUTH_NOAUTH
        external_ogc_server.auth = OGCSERVER_AUTH_NOAUTH

        self.metadata = None
        self.layer_ids = []

        self.layer_group_1 = LayerGroup(name="__test_layer_group_1")

        theme = Theme(name="__test_theme")
        theme.interfaces = [self.main]
        theme.children = [self.layer_group_1]

        DBSession.add_all([self.main, self.user, self.role, theme, self.layer_group_1])

        transaction.commit()
    def teardown_method(self, _):
        testing.tearDown()

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import Layer, Theme, LayerGroup, OGCServer, Interface

        for layer in DBSession.query(Layer).all():
            DBSession.delete(layer)
        for g in DBSession.query(LayerGroup).all():
            DBSession.delete(g)
        for t in DBSession.query(Theme).all():
            DBSession.delete(t)
        DBSession.query(OGCServer).delete()
        DBSession.query(Interface).filter(
            Interface.name == "main"
        ).delete()

        transaction.commit()
Example #59
0
def xsd_sequence_callback(tb, cls):
    from c2cgeoportal_commons.models import DBSession
    for k, p in cls.__dict__.items():
        if not isinstance(p, _AssociationProxy):
            continue
        relationship_property = class_mapper(cls) \
            .get_property(p.target)
        target_cls = relationship_property.argument
        query = DBSession.query(getattr(target_cls, p.value_attr))
        attrs = {}
        attrs["minOccurs"] = "0"
        attrs["nillable"] = "true"
        attrs["name"] = k
        with tag(tb, "xsd:element", attrs) as tb:
            with tag(tb, "xsd:simpleType") as tb:
                with tag(tb, "xsd:restriction",
                         {"base": "xsd:string"}) as tb:
                    for value, in query:
                        with tag(tb, "xsd:enumeration", {"value": value}):
                            pass
Example #60
0
    def fulltextsearch(self):
        lang = locale_negotiator(self.request)

        try:
            language = self.languages[lang]
        except KeyError:
            return HTTPInternalServerError(
                detail="{0!s} not defined in languages".format(lang))

        if "query" not in self.request.params:
            return HTTPBadRequest(detail="no query")
        terms = self.request.params.get("query")

        maxlimit = self.settings.get("maxlimit", 200)

        try:
            limit = int(self.request.params.get(
                "limit",
                self.settings.get("defaultlimit", 30)))
        except ValueError:
            return HTTPBadRequest(detail="limit value is incorrect")
        if limit > maxlimit:
            limit = maxlimit

        try:
            partitionlimit = int(self.request.params.get("partitionlimit", 0))
        except ValueError:
            return HTTPBadRequest(detail="partitionlimit value is incorrect")
        if partitionlimit > maxlimit:
            partitionlimit = maxlimit

        terms_ts = "&".join(w + ":*"
                            for w in IGNORED_CHARS_RE.sub(" ", terms).split(" ") if w != "")
        _filter = FullTextSearch.ts.op("@@")(func.to_tsquery(language, terms_ts))

        if self.request.user is None or self.request.user.role is None:
            _filter = and_(_filter, FullTextSearch.public.is_(True))
        else:
            _filter = and_(
                _filter,
                or_(
                    FullTextSearch.public.is_(True),
                    FullTextSearch.role_id.is_(None),
                    FullTextSearch.role_id == self.request.user.role.id
                )
            )

        if "interface" in self.request.params:
            _filter = and_(_filter, or_(
                FullTextSearch.interface_id.is_(None),
                FullTextSearch.interface_id == self._get_interface_id(
                    self.request.params["interface"]
                )
            ))
        else:
            _filter = and_(_filter, FullTextSearch.interface_id.is_(None))

        _filter = and_(_filter, or_(
            FullTextSearch.lang.is_(None),
            FullTextSearch.lang == lang,
        ))

        # The numbers used in ts_rank_cd() below indicate a normalization method.
        # Several normalization methods can be combined using |.
        # 2 divides the rank by the document length
        # 8 divides the rank by the number of unique words in document
        # By combining them, shorter results seem to be preferred over longer ones
        # with the same ratio of matching words. But this relies only on testing it
        # and on some assumptions about how it might be calculated
        # (the normalization is applied two times with the combination of 2 and 8,
        # so the effect on at least the one-word-results is therefore stronger).
        rank = func.ts_rank_cd(FullTextSearch.ts, func.to_tsquery(language, terms_ts), 2 | 8)

        if partitionlimit:
            # Here we want to partition the search results based on
            # layer_name and limit each partition.
            row_number = func.row_number().over(
                partition_by=FullTextSearch.layer_name,
                order_by=(desc(rank), FullTextSearch.label)
            ).label("row_number")
            subq = DBSession.query(FullTextSearch) \
                .add_columns(row_number).filter(_filter).subquery()
            query = DBSession.query(
                subq.c.id, subq.c.label, subq.c.params, subq.c.layer_name,
                subq.c.the_geom, subq.c.actions
            )
            query = query.filter(subq.c.row_number <= partitionlimit)
        else:
            query = DBSession.query(FullTextSearch).filter(_filter)
            query = query.order_by(desc(rank))
            query = query.order_by(FullTextSearch.label)

        query = query.limit(limit)
        objs = query.all()

        features = []
        for o in objs:
            properties = {
                "label": o.label,
            }
            if o.layer_name is not None:
                properties["layer_name"] = o.layer_name
            if o.params is not None:
                properties["params"] = o.params
            if o.actions is not None:
                properties["actions"] = o.actions
            if o.actions is None and o.layer_name is not None:
                properties["actions"] = [{
                    "action": "add_layer",
                    "data": o.layer_name,
                }]

            if o.the_geom is not None:
                geom = to_shape(o.the_geom)
                feature = Feature(
                    id=o.id, geometry=geom,
                    properties=properties, bbox=geom.bounds
                )
                features.append(feature)
            else:
                feature = Feature(
                    id=o.id, properties=properties
                )
                features.append(feature)

        # TODO: add callback function if provided in self.request, else return geojson
        return FeatureCollection(features)