diff --git a/VERSION b/VERSION index 0b2eb36f5..c1e43e6d4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.7.2 +3.7.3 diff --git a/doc/aerospike.rst b/doc/aerospike.rst index f91fef53a..8dc4a3474 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -1734,6 +1734,10 @@ Permission codes define the type of permission granted for a user's role. The user is granted read access. +.. data:: PRIV_WRITE + + The user is granted write access. + .. data:: PRIV_READ_WRITE The user is granted read and write access. diff --git a/doc/exception.rst b/doc/exception.rst index 0b8913a91..1a37624c1 100644 --- a/doc/exception.rst +++ b/doc/exception.rst @@ -84,6 +84,11 @@ Exception Types Protocol-level error. Subclass of :py:exc:`~aerospike.exception.ServerError`. +.. py:exception:: OpNotApplicable + + The operation cannot be applied to the current bin value on the server. + Subclass of :py:exc:`~aerospike.exception.ServerError`. + .. py:exception:: ServerFull The server node is running out of memory and/or storage device space @@ -396,4 +401,4 @@ Exception Hierarchy | +-- NotAuthenticated (80) +-- UDFError (*) +-- UDFNotFound (1301) - +-- LuaFileNotFound (1302) \ No newline at end of file + +-- LuaFileNotFound (1302) diff --git a/setup.py b/setup.py index 2eccfc1a3..13630eb80 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ os.environ['ARCHFLAGS'] = '-arch x86_64' AEROSPIKE_C_VERSION = os.getenv('AEROSPIKE_C_VERSION') if not AEROSPIKE_C_VERSION: - AEROSPIKE_C_VERSION = '4.6.3' + AEROSPIKE_C_VERSION = '4.6.5' DOWNLOAD_C_CLIENT = os.getenv('DOWNLOAD_C_CLIENT') AEROSPIKE_C_HOME = os.getenv('AEROSPIKE_C_HOME') PREFIX = None diff --git a/src/include/exception_types.h b/src/include/exception_types.h index 6ba4d130d..03242acdb 100644 --- a/src/include/exception_types.h +++ b/src/include/exception_types.h @@ -35,6 +35,7 @@ struct exceptions { PyObject *ForbiddenError; PyObject *QueryError; PyObject *InvalidGeoJSON; + PyObject *OpNotApplicable; //26 PyObject *ScanAbortedError; //15 PyObject *ElementNotFoundError; //23 PyObject *ElementExistsError; //24 @@ -110,9 +111,9 @@ struct exceptions { }; struct server_exceptions_struct { - PyObject * *server_exceptions[10]; - char * server_exceptions_name[10]; - int server_exceptions_codes[10]; + PyObject * *server_exceptions[11]; + char * server_exceptions_name[11]; + int server_exceptions_codes[11]; }; struct record_exceptions_struct { PyObject * *record_exceptions[8]; diff --git a/src/main/aerospike.c b/src/main/aerospike.c index 1f7d5087f..1ac2ceddd 100644 --- a/src/main/aerospike.c +++ b/src/main/aerospike.c @@ -94,7 +94,7 @@ AerospikeConstants operator_constants[] = { MOD_INIT(aerospike) { - const char version[8] = "3.7.2"; + const char version[8] = "3.7.3"; // Makes things "thread-safe" PyEval_InitThreads(); int i = 0; diff --git a/src/main/exception.c b/src/main/exception.c index 3bb825c77..9dac4782c 100644 --- a/src/main/exception.c +++ b/src/main/exception.c @@ -40,12 +40,13 @@ PyObject * AerospikeException_New(void) {&exceptions_array.InvalidRequest, &exceptions_array.ServerFull, &exceptions_array.AlwaysForbidden, &exceptions_array.UnsupportedFeature, &exceptions_array.DeviceOverload, &exceptions_array.NamespaceNotFound, &exceptions_array.ForbiddenError, &exceptions_array.QueryError, &exceptions_array.ClusterError, - &exceptions_array.InvalidGeoJSON}, + &exceptions_array.InvalidGeoJSON, &exceptions_array.OpNotApplicable}, {"InvalidRequest", "ServerFull", "AlwaysForbidden", "UnsupportedFeature", "DeviceOverload", "NamespaceNotFound", - "ForbiddenError", "QueryError", "ClusterError", "InvalidGeoJSON"}, + "ForbiddenError", "QueryError", "ClusterError", "InvalidGeoJSON", "OpNotApplicable"}, {AEROSPIKE_ERR_REQUEST_INVALID, AEROSPIKE_ERR_SERVER_FULL, AEROSPIKE_ERR_ALWAYS_FORBIDDEN, AEROSPIKE_ERR_UNSUPPORTED_FEATURE, AEROSPIKE_ERR_DEVICE_OVERLOAD, AEROSPIKE_ERR_NAMESPACE_NOT_FOUND, - AEROSPIKE_ERR_FAIL_FORBIDDEN, AEROSPIKE_ERR_QUERY, AEROSPIKE_ERR_CLUSTER, AEROSPIKE_ERR_GEO_INVALID_GEOJSON} + AEROSPIKE_ERR_FAIL_FORBIDDEN, AEROSPIKE_ERR_QUERY, AEROSPIKE_ERR_CLUSTER, AEROSPIKE_ERR_GEO_INVALID_GEOJSON, + AEROSPIKE_ERR_OP_NOT_APPLICABLE} }; struct record_exceptions_struct record_array = { diff --git a/src/main/policy.c b/src/main/policy.c index cc091485a..bc04564cb 100644 --- a/src/main/policy.c +++ b/src/main/policy.c @@ -145,6 +145,7 @@ AerospikeConstants aerospike_constants[] = { { AS_PRIVILEGE_SYS_ADMIN , "PRIV_SYS_ADMIN" }, { AS_PRIVILEGE_DATA_ADMIN , "PRIV_DATA_ADMIN" }, { AS_PRIVILEGE_READ , "PRIV_READ"}, + { AS_PRIVILEGE_WRITE , "PRIV_WRITE"}, { AS_PRIVILEGE_READ_WRITE , "PRIV_READ_WRITE"}, { AS_PRIVILEGE_READ_WRITE_UDF , "PRIV_READ_WRITE_UDF"}, diff --git a/test/new_tests/test_admin_create_role.py b/test/new_tests/test_admin_create_role.py index a68e50f51..6b22612a3 100644 --- a/test/new_tests/test_admin_create_role.py +++ b/test/new_tests/test_admin_create_role.py @@ -90,6 +90,37 @@ def test_create_role_positive_with_policy(self): self.client.admin_drop_user("testcreaterole") + def test_create_role_positive_with_policy_write(self): + """ + Create role with write privilege positive + """ + try: + self.client.admin_query_role("usr-sys-admin-test") + # role exists, clear it out. + self.client.admin_drop_role("usr-sys-admin-test") + time.sleep(2) + except e.InvalidRole: + pass # we are good, no such role exists + + self.client.admin_create_role("usr-sys-admin-test", + [{"code": aerospike.PRIV_WRITE, + "ns": "test", "set": "demo"}], + {'timeout': 1000}) + time.sleep(1) + roles = self.client.admin_query_role("usr-sys-admin-test") + assert roles == [{'code': 13, 'ns': 'test', 'set': 'demo'}] + + status = self.client.admin_create_user( + "testcreaterole", "createrole", ["usr-sys-admin-test"]) + + assert status == 0 + time.sleep(1) + roles = self.client.admin_query_user("testcreaterole") + + assert roles == ["usr-sys-admin-test"] + + self.client.admin_drop_user("testcreaterole") + def test_create_role_positive(self): """ Create role positive diff --git a/test/new_tests/test_admin_drop_role.py b/test/new_tests/test_admin_drop_role.py index 878097efd..300806616 100644 --- a/test/new_tests/test_admin_drop_role.py +++ b/test/new_tests/test_admin_drop_role.py @@ -81,6 +81,33 @@ def test_drop_role_positive_with_policy(self): with pytest.raises(e.InvalidRole): self.client.admin_query_role("usr-sys-admin-test") + def test_drop_role_positive_with_policy_write(self): + """ + Drop write role positive with policy + """ + try: + self.client.admin_query_role("usr-sys-admin-test") + # role exists, clear it out. + self.client.admin_drop_role("usr-sys-admin-test") + time.sleep(1) + except e.InvalidRole: + pass # we are good, no such role exists + + self.client.admin_create_role("usr-sys-admin-test", + [{"code": aerospike.PRIV_WRITE, + "ns": "test", "set": "demo"}], + {'timeout': 1000}) + time.sleep(1) + + status = self.client.admin_drop_role( + "usr-sys-admin-test", {'timeout': 1000}) + + assert status == 0 + time.sleep(1) + + with pytest.raises(e.InvalidRole): + self.client.admin_query_role("usr-sys-admin-test") + def test_drop_role_positive(self): """ Drop role positive diff --git a/test/new_tests/test_admin_grant_privileges.py b/test/new_tests/test_admin_grant_privileges.py index 1d3df3a56..5c5618483 100644 --- a/test/new_tests/test_admin_grant_privileges.py +++ b/test/new_tests/test_admin_grant_privileges.py @@ -79,6 +79,27 @@ def test_admin_grant_privileges_positive(self): assert status == 0 + def test_admin_grant_privileges_positive_write(self): + """ + Grant write privileges positive + """ + status = self.client.admin_grant_privileges( + "usr-sys-admin-test", + [{"code": aerospike.PRIV_WRITE}]) + + assert status == 0 + time.sleep(1) + roles = self.client.admin_query_role("usr-sys-admin-test") + assert roles == [{'code': 0, 'ns': '', 'set': ''}, + {'code': 1, 'ns': '', 'set': ''}, + {'code': 13, 'ns': '', 'set': ''}] + + status = self.client.admin_revoke_privileges( + "usr-sys-admin-test", + [{"code": aerospike.PRIV_WRITE}]) + + assert status == 0 + def test_admin_grant_privileges_positive_with_policy(self): """ Grant privileges positive with policy diff --git a/test/new_tests/test_admin_revoke_privileges.py b/test/new_tests/test_admin_revoke_privileges.py index b1d4cce77..29a95e312 100644 --- a/test/new_tests/test_admin_revoke_privileges.py +++ b/test/new_tests/test_admin_revoke_privileges.py @@ -80,6 +80,31 @@ def test_admin_revoke_privileges_positive(self): assert roles == [{'code': 0, 'ns': '', 'set': ''}, {'code': 1, 'ns': '', 'set': ''}] + def test_admin_revoke_privileges_positive_write(self): + """ + revoke write privileges positive + """ + status = self.client.admin_grant_privileges( + "usr-sys-admin-test", + [{"code": aerospike.PRIV_WRITE}]) + + assert status == 0 + time.sleep(2) + roles = self.client.admin_query_role("usr-sys-admin-test") + assert roles == [{'code': 0, 'ns': '', 'set': ''}, + {'code': 1, 'ns': '', 'set': ''}, + {'code': 13, 'ns': '', 'set': ''}] + + status = self.client.admin_revoke_privileges( + "usr-sys-admin-test", + [{"code": aerospike.PRIV_WRITE}]) + + assert status == 0 + time.sleep(2) + roles = self.client.admin_query_role("usr-sys-admin-test") + assert roles == [{'code': 0, 'ns': '', 'set': ''}, + {'code': 1, 'ns': '', 'set': ''}] + def test_admin_revoke_privileges_positive_with_policy(self): """ Revoke privileges positive with policy @@ -105,6 +130,31 @@ def test_admin_revoke_privileges_positive_with_policy(self): assert roles == [{'code': 0, 'ns': '', 'set': ''}, {'code': 1, 'ns': '', 'set': ''}] + def test_admin_revoke_privileges_positive_with_policy_write(self): + """ + Revoke write privileges positive with policy + """ + status = self.client.admin_grant_privileges( + "usr-sys-admin-test", + [{"code": aerospike.PRIV_WRITE}], {'timeout': 1000}) + + assert status == 0 + time.sleep(2) + roles = self.client.admin_query_role("usr-sys-admin-test") + assert roles == [{'code': 0, 'ns': '', 'set': ''}, + {'code': 1, 'ns': '', 'set': ''}, + {'code': 13, 'ns': '', 'set': ''}] + + status = self.client.admin_revoke_privileges( + "usr-sys-admin-test", + [{"code": aerospike.PRIV_WRITE}], {'timeout': 1000}) + + time.sleep(1) + assert status == 0 + roles = self.client.admin_query_role("usr-sys-admin-test") + assert roles == [{'code': 0, 'ns': '', 'set': ''}, + {'code': 1, 'ns': '', 'set': ''}] + def test_admin_revoke_privileges_positive_with_ns_set(self): """ Revoke privileges positive with ns and set diff --git a/test/new_tests/test_index.py b/test/new_tests/test_index.py index f665a3afe..8be33d034 100644 --- a/test/new_tests/test_index.py +++ b/test/new_tests/test_index.py @@ -6,7 +6,6 @@ from .index_helpers import ensure_dropped_index from aerospike import exception as e - aerospike = pytest.importorskip("aerospike") try: import aerospike @@ -14,7 +13,6 @@ print("Please install aerospike python client.") sys.exit(1) - class TestIndex(object): @pytest.fixture(autouse=True) diff --git a/test/new_tests/test_list_get.py b/test/new_tests/test_list_get.py index 20a548113..c37409651 100644 --- a/test/new_tests/test_list_get.py +++ b/test/new_tests/test_list_get.py @@ -160,8 +160,8 @@ def test_neg_list_get_with_negative_index(self): key = ('test', 'demo', 1) try: self.as_connection.list_get(key, "contact_no", -56) - except e.InvalidRequest as exception: - assert exception.code == 4 + except e.OpNotApplicable as exception: + assert exception.code == 26 def test_neg_list_get_meta_type_integer(self): """ diff --git a/test/new_tests/test_list_insert.py b/test/new_tests/test_list_insert.py index 370debf48..f1d97a79c 100644 --- a/test/new_tests/test_list_insert.py +++ b/test/new_tests/test_list_insert.py @@ -247,8 +247,8 @@ def test_neg_list_insert_index_negative(self): try: self.as_connection.list_insert(key, "age", -6, False) - except e.InvalidRequest as exception: - assert exception.code == 4 + except e.OpNotApplicable as exception: + assert exception.code == 26 def test_neg_list_insert_index_type_string(self): """ diff --git a/test/new_tests/test_list_pop.py b/test/new_tests/test_list_pop.py index e1549b1c4..399a7e5d6 100644 --- a/test/new_tests/test_list_pop.py +++ b/test/new_tests/test_list_pop.py @@ -180,8 +180,8 @@ def test_neg_list_pop_with_negative_index(self): key = ('test', 'demo', 1) try: self.as_connection.list_pop(key, "contact_no", -56) - except e.InvalidRequest as exception: - assert exception.code == 4 + except e.OpNotApplicable as exception: + assert exception.code == 26 def test_neg_list_pop_meta_type_integer(self): """ diff --git a/test/new_tests/test_list_remove.py b/test/new_tests/test_list_remove.py index 7bc4a3959..433bc058a 100644 --- a/test/new_tests/test_list_remove.py +++ b/test/new_tests/test_list_remove.py @@ -188,8 +188,8 @@ def test_neg_list_remove_with_negative_index(self): key = ('test', 'demo', 1) try: self.as_connection.list_remove(key, "contact_no", -56) - except e.InvalidRequest as exception: - assert exception.code == 4 + except e.OpNotApplicable as exception: + assert exception.code == 26 def test_neg_list_remove_meta_type_integer(self): """ diff --git a/test/new_tests/test_list_set.py b/test/new_tests/test_list_set.py index f7724a22b..567124d57 100644 --- a/test/new_tests/test_list_set.py +++ b/test/new_tests/test_list_set.py @@ -244,8 +244,8 @@ def test_neg_list_set_with_negative_index(self): key = ('test', 'demo', 1) try: self.as_connection.list_set(key, "contact_no", -56, 12) - except e.InvalidRequest as exception: - assert exception.code == 4 + except e.OpNotApplicable as exception: + assert exception.code == 26 def test_neg_list_set_meta_type_integer(self): """ diff --git a/test/new_tests/test_operate.py b/test/new_tests/test_operate.py index ca64d486e..d866d383b 100644 --- a/test/new_tests/test_operate.py +++ b/test/new_tests/test_operate.py @@ -1200,8 +1200,8 @@ def test_neg_operate_list_invalid_requests(self, list): key = ('test', 'demo', 'list_key') try: key, _, _ = self.as_connection.operate(key, list) - except e.InvalidRequest as exception: - assert exception.code == 4 + except e.OpNotApplicable as exception: + assert exception.code == 26 def test_neg_operate_with_command_invalid(self): """ diff --git a/test/new_tests/test_operate_helpers.py b/test/new_tests/test_operate_helpers.py index 1b063d002..15467d4ec 100644 --- a/test/new_tests/test_operate_helpers.py +++ b/test/new_tests/test_operate_helpers.py @@ -922,7 +922,7 @@ def test_neg_operate_list_invalid_requests(self, list): Invoke operate() with list addition operations negative """ key = ('test', 'demo', 'list_key') - with pytest.raises(e.InvalidRequest): + with pytest.raises(e.OpNotApplicable): self.as_connection.operate(key, list)