Skip to content

Commit

Permalink
Add new API changes for user access management and indexes
Browse files Browse the repository at this point in the history
  • Loading branch information
joowani committed Jul 30, 2017
1 parent d754590 commit 2d4164b
Show file tree
Hide file tree
Showing 24 changed files with 521 additions and 310 deletions.
44 changes: 25 additions & 19 deletions arango/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ def run_tests(self, tests): # pragma: no cover
raise ServerRunTestsError(res)
return res.body

def execute(self, program):
def execute(self, program): # pragma: no cover
"""Execute a Javascript program on the server.
:param program: the body of the Javascript program to execute.
Expand Down Expand Up @@ -875,35 +875,42 @@ def delete_user(self, username, ignore_missing=False):
return False
raise UserDeleteError(res)

def user_access(self, username):
"""Return the database access details of a user.
def user_access(self, username, full=False):
"""Return a user's access details for databases (and collections).
:param username: the name of the user
:param username: The name of the user.
:type username: str | unicode
:returns: the names of the databases the user can access
:rtype: [str]
:raises: arango.exceptions.UserAccessError: if the retrieval fails
:param full: Return the full set of access levels for all databases and
collections for the user.
:type full: bool
:returns: The names of the databases (and collections) the user has
access to.
:rtype: [str] | [unicode]
:raises: arango.exceptions.UserAccessError: If the retrieval fails.
.. note::
Only the root user can access this method. For non-root users,
use :func:`arango.database.Database.user_access` (via a database
the users have access to) instead.
"""
res = self._conn.get('/_api/user/{}/database'.format(username))
res = self._conn.get(
'/_api/user/{}/database'.format(username),
params={'full': full}
)
if res.status_code in HTTP_OK:
return list(res.body['result'])
raise UserAccessError(res)

def grant_user_access(self, username, database):
"""Grant user access to a database.
:param username: the name of the user
:param username: The name of the user.
:type username: str | unicode
:param database: the name of the database
:param database: The name of the database.
:type database: str | unicode
:returns: whether the operation was successful
:returns: Whether the operation was successful or not.
:rtype: bool
:raises arango.exceptions.UserGrantAccessError: if the operation fails
:raises arango.exceptions.UserGrantAccessError: If the operation fails.
.. note::
Only the root user can access this method. For non-root users,
Expand All @@ -921,22 +928,21 @@ def grant_user_access(self, username, database):
def revoke_user_access(self, username, database):
"""Revoke user access to a database.
:param username: the name of the user
:param username: The name of the user.
:type username: str | unicode
:param database: the name of the database
:param database: The name of the database.
:type database: str | unicode | unicode
:returns: whether the operation was successful
:returns: Whether the operation was successful or not.
:rtype: bool
:raises arango.exceptions.UserRevokeAccessError: if the operation fails
:raises arango.exceptions.UserRevokeAccessError: If the operation fails.
.. note::
Only the root user can access this method. For non-root users,
use :func:`arango.database.Database.revoke_user_access` (via a
database the users have access to) instead.
"""
res = self._conn.put(
'/_api/user/{}/database/{}'.format(username, database),
data={'grant': 'none'}
res = self._conn.delete(
'/_api/user/{}/database/{}'.format(username, database)
)
if res.status_code in HTTP_OK:
return True
Expand Down
122 changes: 120 additions & 2 deletions arango/collections/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ def name(self):
"""
return self._name

@property
def database(self):
"""Return the name of the database the collection belongs to.
:returns: The name of the database.
:rtype: str | unicode
"""
return self._conn.database

@api_method
def rename(self, new_name):
"""Rename the collection.
Expand Down Expand Up @@ -930,6 +939,7 @@ def indexes(self):
:rtype: [dict]
:raises arango.exceptions.IndexListError: if the list of indexes
cannot be retrieved
"""
request = Request(
method='get',
Expand Down Expand Up @@ -988,7 +998,11 @@ def handler(res):
return request, handler

@api_method
def add_hash_index(self, fields, unique=None, sparse=None):
def add_hash_index(self,
fields,
unique=None,
sparse=None,
deduplicate=None):
"""Create a new hash index in the collection.
:param fields: the document fields to index
Expand All @@ -997,6 +1011,14 @@ def add_hash_index(self, fields, unique=None, sparse=None):
:type unique: bool
:param sparse: index ``None``'s
:type sparse: bool
:param deduplicate: Controls whether inserting duplicate index values
from the same document into a unique array index leads to a unique
constraint error or not. If set to ``True`` (default), only a
single instance of each non-unique index values is inserted into
the index per document. Trying to insert a value into the index
that already exists will always fail, regardless of the value of
this field.
:param deduplicate: bool
:returns: the details on the new index
:rtype: dict
:raises arango.exceptions.IndexCreateError: if the hash index cannot
Expand All @@ -1007,10 +1029,16 @@ def add_hash_index(self, fields, unique=None, sparse=None):
data['unique'] = unique
if sparse is not None:
data['sparse'] = sparse
if deduplicate is not None:
data['deduplicate'] = deduplicate
return self._add_index(data)

@api_method
def add_skiplist_index(self, fields, unique=None, sparse=None):
def add_skiplist_index(self,
fields,
unique=None,
sparse=None,
deduplicate=None):
"""Create a new skiplist index in the collection.
A skiplist index is used to find the ranges of documents (e.g. time).
Expand All @@ -1021,6 +1049,14 @@ def add_skiplist_index(self, fields, unique=None, sparse=None):
:type unique: bool
:param sparse: index ``None``'s
:type sparse: bool
:param deduplicate: Controls whether inserting duplicate index values
from the same document into a unique array index leads to a unique
constraint error or not. If set to ``True`` (default), only a
single instance of each non-unique index values is inserted into
the index per document. Trying to insert a value into the index
that already exists will always fail, regardless of the value of
this field.
:param deduplicate: bool
:returns: the details on the new index
:rtype: dict
:raises arango.exceptions.IndexCreateError: if the skiplist index
Expand All @@ -1031,6 +1067,8 @@ def add_skiplist_index(self, fields, unique=None, sparse=None):
data['unique'] = unique
if sparse is not None:
data['sparse'] = sparse
if deduplicate is not None:
data['deduplicate'] = deduplicate
return self._add_index(data)

@api_method
Expand Down Expand Up @@ -1138,3 +1176,83 @@ def handler(res):
return not res.body['error']

return request, handler

@api_method
def user_access(self, username):
"""Return a user's access details for the collection.
Appropriate permissions are required in order to execute this method.
:param username: The name of the user.
:type username: str | unicode
:returns: The access details (e.g. ``"rw"``, ``None``)
:rtype: str | unicode | None
:raises: arango.exceptions.UserAccessError: If the retrieval fails.
"""
request = Request(
method='get',
endpoint='/_api/user/{}/database/{}/{}'.format(
username, self.database, self.name
)
)

def handler(res):
if res.status_code in HTTP_OK:
result = res.body['result'].lower()
return None if result == 'none' else result
raise UserAccessError(res)

return request, handler

@api_method
def grant_user_access(self, username):
"""Grant user access to the collection.
Appropriate permissions are required in order to execute this method.
:param username: The name of the user.
:type username: str | unicode
:returns: Whether the operation was successful or not.
:rtype: bool
:raises arango.exceptions.UserGrantAccessError: If the operation fails.
"""
request = Request(
method='put',
endpoint='/_api/user/{}/database/{}/{}'.format(
username, self.database, self.name
),
data={'grant': 'rw'}
)

def handler(res):
if res.status_code in HTTP_OK:
return True
raise UserGrantAccessError(res)

return request, handler

@api_method
def revoke_user_access(self, username):
"""Revoke user access to the collection.
Appropriate permissions are required in order to execute this method.
:param username: The name of the user.
:type username: str | unicode
:returns: Whether the operation was successful or not.
:rtype: bool
:raises arango.exceptions.UserRevokeAccessError: If the operation fails.
"""
request = Request(
method='delete',
endpoint='/_api/user/{}/database/{}/{}'.format(
username, self.database, self.name
)
)

def handler(res):
if res.status_code in HTTP_OK:
return True
raise UserRevokeAccessError(res)

return request, handler
1 change: 0 additions & 1 deletion arango/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ def type(self):
def handle_request(self, request, handler):
# from arango.async import AsyncExecution
# from arango.exceptions import ArangoError
#
# async = AsyncExecution(self, return_result=True)
# response = async.handle_request(request, handler)
# while response.status() != 'done':
Expand Down
Loading

0 comments on commit 2d4164b

Please sign in to comment.