Source code for franz.openrdf.sail.allegrographserver

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable-msg=C0103

################################################################################
# Copyright (c) 2006-2017 Franz Inc.  
# All rights reserved. This program and the accompanying materials are
# made available under the terms of the MIT License which accompanies
# this distribution, and is available at http://opensource.org/licenses/MIT
################################################################################

from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import str
from builtins import map
from builtins import object
from future import standard_library
standard_library.install_aliases()

import os

from ..exceptions import ServerException
from ..repository.repository import Repository, RepositoryConnection
from ...miniclient import repository as miniserver
import re, urllib.request, urllib.parse, urllib.error
from . import spec
from past.builtins import basestring

READ_ONLY = 'READ_ONLY'

LEGAL_OPTION_TYPES = {READ_ONLY: bool,}

[docs]class AllegroGraphServer(object): """ The AllegroGraphServer object represents a remote AllegroGraph server on the network. It is used to inventory and access the catalogs of that server. """
[docs] def __init__(self, host=None, port=None, user=None, password=None, cainfo=None, sslcert=None, verifyhost=None, verifypeer=None, protocol=None, proxy=os.environ.get('AGRAPH_PROXY'), **options): """ Define the connection to the AllegroGraph HTTP server. Pass either ``user`` and ``password`` for Basic Authentication or ``cainfo``, ``sslcert`` values for client X.509 certificate authentication. :param host: Address of the AllegroGraph server to connect to. This can also include protocol and port (e.g. http://localhost:10035). Can also be specified in the ``AGRAPH_HOST`` environment variable. The default value is ``'localhost'```. :type host: string :param port: Port on which the server is listening. The default is 10035 if ``protocol`` is ``'http'`` and 10036 if it is ``'https'``. Can also be specified in the ``AGRAPH_PORT`` environment variable. If passed explicitly this parameter overrides any value that might have been specified as a part of the ``host`` string. :type port: int :param protocol: Connection protocol, either ``'http'`` or ``'https'``. The default is ``'http'`` if no SSL parameters are set and `'https'`` otherwise. If passed explicitly this parameter overrides any value that might have been specified as a part of the ``host`` string. :type protocol: string :param user: Username (when using Basic authentication). Can also be specified in the ``AGRAPH_USER`` environment variable. :type user: string :param password: Password (when using Basic authentication). Can also be specified in the ``AGRAPH_PASSWORD`` environment variable. :type password: string :param cainfo: Path to a file or directory containing CA certificates that will be used to validate the server's certificate. :type cainfo: string :param sslcert: Client certificate path (when using SSL authentication). :type sslcert: string :param verifyhost: If set to ``0`` it will not be an error if the server's SSL certificate does not match the server's address. The default value is ``2``, meaning that the host name will be validated against the certificate. ..seealso:: https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html :type verifyhost: int :param verifypeer: If set to ``1`` (the default) the validity of the server's SSL certificate will be checked. Set to ``0`` to disable the validation. ..seealso:: https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html :type verifypeer: int :param proxy: Proxy specification string. The format is SCHEME://HOST:PORT. Supported schemes are 'http', 'socks4' and 'socks5'. Note that for SOCKS proxies DNS requests are performed by the proxy server. The default value is taken from the AGRAPH_PROXY environment variable. :type proxy: string :param options: Ignored. """ # Not sure why we accept these, but don't want to change the API at this point. del options host = host or os.environ.get('AGRAPH_HOST', '127.0.0.1') # Check if other arguments were passed as a part of host match = re.match(r'^(?:(?P<protocol>https?)://)?' r'(?P<host>[^:]*)(?::(?P<port>[0-9]*))?(?P<tail>.*)$', host) if match: if protocol is None: protocol = match.group('protocol') if port is None and match.group('port') is not None: port = int(match.group('port')) host = match.group('host') tail = match.group('tail') else: tail = '' has_https_params = cainfo or sslcert or verifyhost is not None or verifypeer is not None if protocol is None: protocol = 'https' if has_https_params else 'http' if port is None: if 'AGRAPH_PORT' in os.environ: port = int(os.environ.get('AGRAPH_PORT')) else: port = 10035 if protocol == 'http' else 10036 uri = '{protocol}://{host}:{port}{tail}'.format(protocol=protocol, host=host, port=port, tail=tail) user = user or os.environ.get('AGRAPH_USER') password = password or os.environ.get('AGRAPH_PASSWORD') self._client = miniserver.Client(uri, user, password, cainfo, sslcert, verifyhost, verifypeer, proxy=proxy)
@property def url(self): """Return the server's URL.""" return self._client.url @property def version(self): """Return the server's version as a string.""" return self._client.getVersion()
[docs] def listCatalogs(self): """ Get the list of catalogs on this server. A value of ``None`` will be included in this list to represent the root catalog. :return: A list of catalog names. :rtype: list[string] """ catalogs = self._client.listCatalogs() return catalogs
[docs] def openCatalog(self, name=None): """ Open a catalog by name. Pass None to open the root catalog. :param name: One of the catalog names from :meth:`listCatalogs` or ``None`` to open the root catalog. :return: A catalog object. :rtype: Catalog """ # Allow for None and '' (or anything else that evaluates to False) to # mean the root catalog. if not name: name = None cats = self.listCatalogs() if not name in cats: raise ServerException("There is no catalog named '%s' (found %s)" % (name, cats)) return Catalog(name, self._client)
[docs] def getInitfile(self): """ Retrieve the contents of the server initialization file. The initialization file is a collection of Common Lisp code that is executed in every back-end as it is created. :return: Init file content. :rtype: string """ return self._client.getInitfile()
[docs] def setInitfile(self, content=None, restart=True): """ Replace the current initialization file contents with the ``content`` string or remove if None. :param content: New init file conent. :type content: string :param restart: specifies whether any running shared back-ends should be shut down, so that subsequent requests will be handled by back-ends that include the new code. Default is ``True``. """ return self._client.setInitfile(content, restart)
[docs] def openSession(self, spec, autocommit=False, lifetime=None, loadinitfile=False): """ Open a session on a federated, reasoning, or filtered store. Use the helper functions in the :mod:`franz.openrdf.sail.spec` module to create the spec string. :param spec: Session :mod:`specification string <franz.openrdf.sail.spec>`. :type spec: string :param autocommit: Autocommit mode (default: ``False`` = start a transaction). :type autocommit: bool :param lifetime: The number of seconds a session can be idle before being terminated. The default value is 300 (5 minutes). The maximum acceptable value is 21600 (6 hours). :param loadinitfile: If ``True`` the init file will be loaded into the new session. The default is False. :type loadinitfile: bool :return: A connection to the new session. :rtype: RepositoryConnection """ minirep = self._client.openSession(spec, autocommit=autocommit, lifetime=lifetime, loadinitfile=loadinitfile) return RepositoryConnection(Repository(None, None, minirep))
[docs] def listScripts(self): """ Return the list of Sitescripts currently on the server. When a user creates a session, they can choose to load one or more of these scripts into the session. :return: A list of script names. :rtype: list[string] """ return self._client.listScripts()
[docs] def addScript(self, module, code): """ Create or replace a sitescript. :param module: Script name. :type module: string :param code: Script content. :type code: string """ return self._client.addScript(module, code)
[docs] def deleteScript(self, module): """ Delete a sitescript. :param module: Script name. :type module: string """ return self._client.deleteScript(module)
[docs] def getScript(self, module): """ Get the body of a sitescript. :param module: Script name. :type module: string :return: Script content. :rtype: string """ return self._client.getScript(module)
[docs] def openFederated(self, repositories, autocommit=False, lifetime=None, loadinitfile=False): """ Open a session that federates several repositories. The repositories argument should be an array containing store designators, which can be Repository or RepositoryConnection objects, strings (naming a store in the root catalog, or the URL of a store), or (storename, catalogname) tuples. :param repositories: List of repositories to federate. :type repositories: list[string|(string, string)|Repository|RepositoryConnection] :param autocommit: Autocommit mode (default: ``False`` = start a transaction). :type autocommit: bool :param lifetime: The number of seconds a session can be idle before being terminated. The default value is 300 (5 minutes). The maximum acceptable value is 21600 (6 hours). :param loadinitfile: If ``True`` the init file will be loaded into the new session. The default is False. :type loadinitfile: bool :return: A connection to the new session. :rtype: RepositoryConnection """ def asRepoString(x): if isinstance(x, basestring): return spec.local(x) elif isinstance(x, tuple): return spec.local(x[0], x[1]) elif isinstance(x, Repository): return x.getSpec() elif isinstance(x, RepositoryConnection): return x.getSpec() else: raise TypeError(str(x) + " is not a valid repository specification.") return self.openSession(spec.federate(*list(map(asRepoString, repositories))), autocommit, lifetime, loadinitfile)
[docs] def listUsers(self): """ Returns a list of names of all the users that have been defined. """ return self._client.listUsers()
[docs] def addUser(self, name, password=None): """ Create a new user. Expects a password parameter, which specifies the user's password (can be left off when creating the anonymous user). """ assert password is not None or name == 'anonymous' self._client.addUser(name, password)
[docs] def deleteUser(self, name): """ Delete a user. """ self._client.deleteUser(name)
[docs] def changeUserPassword(self, name, password): """ Change the password for the given user. """ self._client.changeUserPassword(name, password)
[docs] def listUserAccess(self, name): """ Retrieve the read/write access for a user. This returns a result set, each element of which has a read, write, catalog, and repository component. The first two are booleans, the latter two strings. For permissions granted globally, catalog and repository will have a value of "*", for those granted per-catalog, only repository will be "*". catalog normally contains the catalog name, but for the rootAs above, but also includes the access granted to roles that this user has. catalog "/" is used. """ return self._client.listUserAccess(name)
[docs] def addUserAccess(self, name, read, write, catalog='*', repository='*'): """ Grant read/write access to a user. :param read: Whether to grant read access. Defaults to ``False``. :type read: bool :param write: Whether to grant write access. Defaults to ``False``. :type write: bool :param catalog: Which catalog to grant the access on. Leave off or pass ``"*"`` to grant access on all catalogs. Use ``"/"`` for the root catalog. :type catalog: string :param repository: Specifies the repository that access is granted on. Passing ``"*"``, or leaving the parameter off, means all repositories in the given catalog. :type repository: string """ self._client.addUserAccess(name, read, write, catalog, repository)
[docs] def deleteUserAccess(self, name, read, write, catalog='*', repository='*'): """ Takes the same parameters as PUT on this URL, but revokes the access instead of granting it. """ self._client.deleteUserAccess(name, read, write, catalog, repository)
[docs] def listUserEffectiveAccess(self, name): """ Like listUserAccess, but also includes the access granted to roles that this user has. """ return self._client.listUserEffectiveAccess(name)
[docs] def listUserPermissions(self, name): """ List the permission flags that have been assigned to a user (any of super, eval, session, replication). """ return self._client.listUserPermissions(name)
[docs] def listUserEffectivePermissions(self, name): """ Retrieve the permission flags assigned to the user, or any of its roles. """ return self._client.listUserEffectivePermissions(name)
[docs] def addUserPermission(self, name, _type): """ Assigns the given permission to this user. type should be super, eval, replication, or session. """ self._client.addUserPermission(name, _type)
[docs] def deleteUserPermission(self, name, _type): """ Revokes the given permission for this user. """ self._client.deleteUserPermission(name, _type)
[docs] def listRoles(self): """ Returns the names of all defined roles. """ return self._client.listRoles()
[docs] def addRole(self, role): """ Creates a new role. """ self._client.addRole(role)
[docs] def deleteRole(self, role): """ Deletes a role. """ self._client.deleteRole(role)
[docs] def listRolePermissions(self, role): """ Lists the permission flags granted to a role. """ return self._client.listRolePermissions(role)
[docs] def addRolePermission(self, role, _type): """ Grants a role a certain permission. type should be super, eval, or session. """ self._client.addRolePermission(role, _type)
[docs] def deleteRolePermission(self, role, _type): """ Revokes a permission for a role. """ self._client.deleteRolePermission(role, _type)
[docs] def listRoleAccess(self, role): """ Query the access granted to a role. Returns a result in the same format as the equivalent service for users. """ return self._client.listRoleAccess(role)
[docs] def addRoleAccess(self, role, read, write, catalog='*', repository='*'): """ Grant read/write access to a role. See here for the parameters that are expected. """ return self._client.addRoleAccess(role, read, write, catalog, repository)
[docs] def deleteRoleAccess(self, role, read, write, catalog='*', repository='*'): """ Revoke read/write access for a role. Accepts the same parameters as above. """ self._client.deleteRoleAccess(role, read, write, catalog, repository)
[docs] def listUserRoles(self, name): """ Retrieves a list of role names for this user name. """ return self._client.listUserRoles(name)
[docs] def addUserRole(self, name, role): """ Add a role to a user. """ self._client.addUserRole(name, role)
[docs] def deleteUserRole(self, name, role): """ Remove a role from a user. """ self._client.deleteUserRole(name, role)
[docs] def listUserSecurityFilters(self, name, _type): """ List security filters for user. _type is one of "allow", "disallow" """ return self._client.listUserSecurityFilters(name, _type)
[docs] def addUserSecurityFilter(self, name, _type, s=None, p=None, o=None, g=None): """ Add a security filter for the user. name - user name _type - one of 'allow' or 'disallow' s - optional subject p - optional predicate o - optional predicate g - optional graph """ self._client.addUserSecurityFilter(name, _type, s, p, o, g)
[docs] def deleteUserSecurityFilter(self, name, _type, s=None, p=None, o=None, g=None): """ Add a security filter for the user. name - user name _type - one of 'allow' or 'disallow' s - optional subject p - optional predicate o - optional predicate g - optional graph """ self._client.deleteUserSecurityFilter(name, _type, s, p, o, g)
[docs] def listRoleSecurityFilters(self, role, _type): """ List security filters for user. _type is one of "allow", "disallow" """ return self._client.listRoleSecurityFilters(role, _type)
[docs] def addRoleSecurityFilter(self, role, _type, s=None, p=None, o=None, g=None): """ Add a security filter for the user. role - role name _type - one of 'allow' or 'disallow' s - optional subject p - optional predicate o - optional predicate g - optional graph """ self._client.addRoleSecurityFilter(role, _type, s, p, o, g)
[docs] def deleteRoleSecurityFilter(self, role, _type, s=None, p=None, o=None, g=None): """ Add a security filter for the user. role - role name _type - one of 'allow' or 'disallow' s - optional subject p - optional predicate o - optional predicate g - optional graph """ self._client.deleteRoleSecurityFilter(role, _type, s, p, o, g)
[docs]class Catalog(object): """ Container of multiple repositories (triple stores). """
[docs] def __init__(self, name, client): self.mini_catalog = client.openCatalogByName(name) self._name = name
[docs] def getName(self): """Return the catalog name.""" return self._name
name = property(getName)
[docs] def listRepositories(self): """ Return a list of names of repositories (triple stores) managed by this catalog. """ return self.mini_catalog.listRepositories()
[docs] def deleteRepository(self, name): """ Delete a repository. :param name: Repository name. :type name: string """ return self.mini_catalog.deleteRepository(name)
[docs] def getRepository(self, name, access_verb): """ Creates or opens a repository. :param name: Repository name. :type name: string :param access_verb: Determines mode of operation. Possible values are: - **Repository.RENEW** clears the contents of an existing repository before opening. If the indicated repository does not exist, it creates one. - **Repository.OPEN** opens an existing repository, or throws an exception if the repository is not found. - **Repository.ACCESS** opens an existing repository, or creates a new one if the repository is not found. - **Repository.CREATE** creates a new repository, or throws an exception if one by that name already exists. :return: A repository object. :rtype: Repository """ access_verb = access_verb.upper() name = urllib.parse.quote_plus(name) exists = name in self.listRepositories(); if access_verb == Repository.RENEW: if exists: self.deleteRepository(name) return self.createRepository(name) if access_verb == Repository.CREATE: if exists: raise ServerException( "Can't create triple store named '%s' because a store with that name already exists.", name) return self.createRepository(name) if access_verb == Repository.OPEN: if not exists: raise ServerException( "Can't open a triple store named '%s' because there is none.", name) return Repository(self, name, self.mini_catalog.getRepository(name)) if access_verb == Repository.ACCESS: if not exists: return self.createRepository(name) return Repository(self, name, self.mini_catalog.getRepository(name))
[docs] def createRepository(self, name, indices=None): """ Creates a new repository with the given name. :param name: Repository name. :type name: string :param indices: If provided, creates a store with the given indices. (e.g. ``["spogi, "gspoi", ...]``) :type indices: list[string] :return: A repository object. :rtype: Repository """ return Repository(self, name, self.mini_catalog.createRepository(name, indices=indices))