diff --git a/Documentation/Books/Manual/Security/Auditing/AuditConfiguration.md b/Documentation/Books/Manual/Security/Auditing/AuditConfiguration.md index 499ceeac0a..c75a2eca1e 100644 --- a/Documentation/Books/Manual/Security/Auditing/AuditConfiguration.md +++ b/Documentation/Books/Manual/Security/Auditing/AuditConfiguration.md @@ -27,4 +27,16 @@ Hostname `--audit.hostname name` The name of the server used in audit log messages. By default the -system hostname is used. \ No newline at end of file +system hostname is used. + +Verbosity +--------- + +`--log.level topic=level` + +By default, the server will log all audit events. Some low-priority events, such +as statistics operations, are logged with the `debug` log level. To keep such +events from cluttering the log, set the appropriate topic to `info`. All other +messages will be logged at the `info` level. Audit topics include +`audit-authentication`, `audit-collection`, `audit-database`, `audit-document`, +`audit-service`, and `audit-view`. diff --git a/Documentation/Books/Manual/Security/Auditing/AuditEvents.md b/Documentation/Books/Manual/Security/Auditing/AuditEvents.md index 5c20faf961..0b63880e6a 100644 --- a/Documentation/Books/Manual/Security/Auditing/AuditEvents.md +++ b/Documentation/Books/Manual/Security/Auditing/AuditEvents.md @@ -12,44 +12,47 @@ Authentication ### Unknown authentication methods ``` -2016-10-03 15:44:23 | server1 | - | database1 | 127.0.0.1:61525 | - | unknown authentication method | /_api/version +2016-10-03 15:44:23 | server1 | audit-authentication | n/a | database1 | 127.0.0.1:61525 | n/a | unknown authentication method | /_api/version ``` ### Missing credentials ``` -2016-10-03 15:39:49 | server1 | - | database1 | 127.0.0.1:61498 | - | credentials missing | /_api/version +2016-10-03 15:39:49 | server1 | audit-authentication | n/a | database1 | 127.0.0.1:61498 | n/a | credentials missing | /_api/version ``` ### Wrong credentials ``` -2016-10-03 15:47:26 | server1 | user1 | database1 | 127.0.0.1:61528 | http basic | credentials wrong | /_api/version +2016-10-03 15:47:26 | server1 | audit-authentication | n/a | database1 | 127.0.0.1:61528 | http basic | credentials wrong | /_api/version ``` +Please note, that the user given as fourth part is the user that requested +the login. In general it will be unavailable. + ### Password change required ``` -2016-10-03 16:18:53 | server1 | user1 | database1 | 127.0.0.1:62257 | - | password change required | /_api/version +2016-10-03 16:18:53 | server1 | audit-authentication | user1 | database1 | 127.0.0.1:62257 | http basic | password change required | /_api/version ``` ### JWT login succeeded ``` -2016-10-03 17:21:22 | server1 | - | database1 | 127.0.0.1:64214 | http jwt | user 'root' authenticated | /_open/auth +2016-10-03 17:21:22 | server1 | audit-authentication | root | database1 | 127.0.0.1:64214 | http jwt | user 'root' authenticated | /_open/auth ``` -Please note, that the user given as third part is the user that requested -the login. In general, it will be empty. +Please note, that the user given as fourth part is the user that requested +the login. ### JWT login failed ``` -2016-10-03 17:21:22 | server1 | - | database1 | 127.0.0.1:64214 | http jwt | user 'root' wrong credentials | /_open/auth +2016-10-03 17:21:22 | server1 | audit-authentication | root | database1 | 127.0.0.1:64214 | http jwt | user 'root' wrong credentials | /_open/auth ``` -Please note, that the user given as third part is the user that requested -the login. In general, it will be empty. +Please note, that the user given as fourth part is the user that requested +the login. Authorization ------------- @@ -57,7 +60,7 @@ Authorization ### User not authorized to access database ``` -2016-10-03 16:20:52 | server1 | user1 | database2 | 127.0.0.1:62262 | http basic | not authorized | /_api/version +2016-10-03 16:20:52 | server1 | audit-authentication | user1 | database2 | 127.0.0.1:62262 | http basic | not authorized | /_api/version ``` Databases @@ -66,13 +69,13 @@ Databases ### Create a database ``` -2016-10-04 15:33:25 | server1 | user1 | database1 | 127.0.0.1:56920 | http basic | create database 'database1' | ok | /_api/database +2016-10-04 15:33:25 | server1 | audit-database | user1 | database1 | 127.0.0.1:56920 | http basic | create database 'database1' | ok | /_api/database ``` ### Drop a database ``` -2016-10-04 15:33:25 | server1 | user1 | database1 | 127.0.0.1:56920 | http basic | delete database 'database1' | ok | /_api/database +2016-10-04 15:33:25 | server1 | audit-database | user1 | database1 | 127.0.0.1:56920 | http basic | delete database 'database1' | ok | /_api/database ``` Collections @@ -81,19 +84,19 @@ Collections ### Create a collection ``` -2016-10-05 17:35:57 | server1 | user1 | database1 | 127.0.0.1:51294 | http basic | create collection 'collection1' | ok | /_api/collection +2016-10-05 17:35:57 | server1 | audit-collection | user1 | database1 | 127.0.0.1:51294 | http basic | create collection 'collection1' | ok | /_api/collection ``` ### Truncate a collection ``` -2016-10-05 17:36:08 | server1 | user1 | database1 | 127.0.0.1:51294 | http basic | truncate collection 'collection1' | ok | /_api/collection/collection1/truncate +2016-10-05 17:36:08 | server1 | audit-collection | user1 | database1 | 127.0.0.1:51294 | http basic | truncate collection 'collection1' | ok | /_api/collection/collection1/truncate ``` ### Drop a collection ``` -2016-10-05 17:36:30 | server1 | user1 | database1 | 127.0.0.1:51294 | http basic | delete collection 'collection1' | ok | /_api/collection/collection1 +2016-10-05 17:36:30 | server1 | audit-collection | user1 | database1 | 127.0.0.1:51294 | http basic | delete collection 'collection1' | ok | /_api/collection/collection1 ``` Indexes @@ -102,13 +105,13 @@ Indexes ### Create a index ``` -2016-10-05 18:19:40 | server1 | user1 | database1 | 127.0.0.1:52467 | http basic | create index in 'collection1' | ok | {"fields":["a"],"sparse":false,"type":"skiplist","unique":false} | /_api/index?collection=collection1 +2016-10-05 18:19:40 | server1 | audit-collection | user1 | database1 | 127.0.0.1:52467 | http basic | create index in 'collection1' | ok | {"fields":["a"],"sparse":false,"type":"skiplist","unique":false} | /_api/index?collection=collection1 ``` ### Drop a index ``` -2016-10-05 18:18:28 | server1 | user1 | database1 | 127.0.0.1:52464 | http basic | drop index ':44051' | ok | /_api/index/collection1/44051 +2016-10-05 18:18:28 | server1 | audit-collection | user1 | database1 | 127.0.0.1:52464 | http basic | drop index 'collection1/44051' | ok | /_api/index/collection1/44051 ``` Documents @@ -117,36 +120,42 @@ Documents ### Reading a single document ``` -2016-10-04 12:27:55 | server1 | user1 | database1 | 127.0.0.1:53699 | http basic | create document ok | /_api/document/collection1 +2016-10-04 12:27:55 | server1 | audit-document | user1 | database1 | 127.0.0.1:53699 | http basic | read document in 'collection1' | ok | /_api/document/collection1 +``` + +### Creating a single document + +``` +2016-10-04 12:27:55 | server1 | audit-document | user1 | database1 | 127.0.0.1:53699 | http basic | create document in 'collection1' | ok | /_api/document/collection1 ``` ### Replacing a single document ``` -2016-10-04 12:28:08 | server1 | user1 | database1 | 127.0.0.1:53699 | http basic | replace document ok | /_api/document/collection1/21456?ignoreRevs=false +2016-10-04 12:28:08 | server1 | audit-document | user1 | database1 | 127.0.0.1:53699 | http basic | replace document 'collection1/21456' | ok | /_api/document/collection1/21456?ignoreRevs=false ``` ### Modifying a single document ``` -2016-10-04 12:28:15 | server1 | user1 | database1 | 127.0.0.1:53699 | http basic | modify document ok | /_api/document/collection1/21456?keepNull=true&ignoreRevs=false +2016-10-04 12:28:15 | server1 | audit-document | user1 | database1 | 127.0.0.1:53699 | http basic | modify document 'collection1/21456' | ok | /_api/document/collection1/21456?keepNull=true&ignoreRevs=false ``` ### Deleting a single document ``` -2016-10-04 12:28:23 | server1 | user1 | database1 | 127.0.0.1:53699 | http basic | delete document ok | /_api/document/collection1/21456?ignoreRevs=false +2016-10-04 12:28:23 | server1 | audit-document | user1 | database1 | 127.0.0.1:53699 | http basic | delete document 'collection1/21456' | ok | /_api/document/collection1/21456?ignoreRevs=false ``` For example, if someones tries to delete a non-existing document, it will be logged as ``` -2016-10-04 12:28:26 | server1 | user1 | database1 | 127.0.0.1:53699 | http basic | delete document failed | /_api/document/collection1/21456?ignoreRevs=false +2016-10-04 12:28:26 | server1 | audit-document | user1 | database1 | 127.0.0.1:53699 | http basic | delete document 'collection/21456' | failed | /_api/document/collection1/21456?ignoreRevs=false ``` Queries ------- ``` -2016-10-06 12:12:10 | server1 | user1 | database1 | 127.0.0.1:54232 | http basic | query document | ok | for i in collection1 return i | /_api/cursor +2016-10-06 12:12:10 | server1 | audit-document | user1 | database1 | 127.0.0.1:54232 | http basic | query document | ok | for i in collection1 return i | /_api/cursor ``` diff --git a/Documentation/DocuBlocks/Rest/Administration/get_admin_modules_flush.md b/Documentation/DocuBlocks/Rest/Administration/get_admin_modules_flush.md index 3c1d20f273..9955e82eae 100644 --- a/Documentation/DocuBlocks/Rest/Administration/get_admin_modules_flush.md +++ b/Documentation/DocuBlocks/Rest/Administration/get_admin_modules_flush.md @@ -231,7 +231,7 @@ One of the possible log levels. @RESTBODYPARAM{audit-view,string,optional,string} One of the possible log levels. -@RESTBODYPARAM{audit-documentation,string,optional,string} +@RESTBODYPARAM{audit-document,string,optional,string} One of the possible log levels. @RESTBODYPARAM{audit-service,string,optional,string} @@ -252,4 +252,3 @@ is returned when an invalid HTTP method is used. is returned if the server cannot generate the result due to an out-of-memory error. @endDocuBlock - diff --git a/arangod/Cluster/ClusterInfo.cpp b/arangod/Cluster/ClusterInfo.cpp index 15960fc161..22398a9a7a 100644 --- a/arangod/Cluster/ClusterInfo.cpp +++ b/arangod/Cluster/ClusterInfo.cpp @@ -1550,6 +1550,7 @@ Result ClusterInfo::createCollectionCoordinator( // create collection StaticStrings::Empty); if (name.empty() || !json.isObject() || !json.get("shards").isObject()) { + events::CreateCollection(databaseName, name, TRI_ERROR_BAD_PARAMETER); return Result(TRI_ERROR_BAD_PARAMETER); // must not be empty } @@ -1566,8 +1567,7 @@ Result ClusterInfo::createCollectionCoordinator( // create collection if (it2 != (*it).second.end()) { // collection already exists! - events::CreateCollection(name, TRI_ERROR_ARANGO_DUPLICATE_NAME); - + events::CreateCollection(databaseName, name, TRI_ERROR_ARANGO_DUPLICATE_NAME); return Result(TRI_ERROR_ARANGO_DUPLICATE_NAME); } } @@ -1580,8 +1580,7 @@ Result ClusterInfo::createCollectionCoordinator( // create collection if (it2 != (*it).second.end()) { // view already exists! - events::CreateView(name, TRI_ERROR_ARANGO_DUPLICATE_NAME); - + events::CreateCollection(databaseName, name, TRI_ERROR_ARANGO_DUPLICATE_NAME); return Result(TRI_ERROR_ARANGO_DUPLICATE_NAME); } } @@ -1592,14 +1591,12 @@ Result ClusterInfo::createCollectionCoordinator( // create collection // mop: why do these ask the agency instead of checking cluster info? if (!ac.exists("Plan/Databases/" + databaseName)) { - events::CreateCollection(name, TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); - + events::CreateCollection(databaseName, name, TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); return Result(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (ac.exists("Plan/Collections/" + databaseName + "/" + collectionID)) { - events::CreateCollection(name, TRI_ERROR_CLUSTER_COLLECTION_ID_EXISTS); - + events::CreateCollection(databaseName, name, TRI_ERROR_CLUSTER_COLLECTION_ID_EXISTS); return Result(TRI_ERROR_CLUSTER_COLLECTION_ID_EXISTS); } @@ -1723,7 +1720,8 @@ Result ClusterInfo::createCollectionCoordinator( // create collection << "Timeout in _create collection" << ": database: " << databaseName << ", collId:" << collectionID << "\njson: " << json.toString() << "\ncould not send transaction to agency."; - + events::CreateCollection(databaseName, name, + TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION_IN_PLAN); return Result(TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION_IN_PLAN); } std::vector opers( @@ -1769,6 +1767,8 @@ Result ClusterInfo::createCollectionCoordinator( // create collection if (!tres.hasKey(std::vector( {AgencyCommManager::path(), "Supervision"}))) { + events::CreateCollection(databaseName, name, + TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION_IN_PLAN); return Result(TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION_IN_PLAN); } @@ -1783,6 +1783,8 @@ Result ClusterInfo::createCollectionCoordinator( // create collection errorMsg += s.value.copyString(); } + events::CreateCollection(databaseName, name, + TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION_IN_PLAN); return Result( // result TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION_IN_PLAN, // code errorMsg // message @@ -1807,8 +1809,8 @@ Result ClusterInfo::createCollectionCoordinator( // create collection continue; } - events::CreateCollection(name, TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION_IN_PLAN); - + events::CreateCollection(databaseName, name, + TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION_IN_PLAN); return Result(TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION_IN_PLAN, // code std::string("file: ") + __FILE__ + " line: " + std::to_string(__LINE__) + " HTTP code: " + std::to_string(res.httpCode()) + @@ -1833,7 +1835,7 @@ Result ClusterInfo::createCollectionCoordinator( // create collection if (numberOfShards == 0 || isSmart) { loadCurrent(); - events::CreateCollection(name, TRI_ERROR_NO_ERROR); + events::CreateCollection(databaseName, name, TRI_ERROR_NO_ERROR); return Result(TRI_ERROR_NO_ERROR); } @@ -1846,7 +1848,7 @@ Result ClusterInfo::createCollectionCoordinator( // create collection CONDITION_LOCKER(locker, agencyCallback->_cv); cbGuard.fire(); // unregister cb before accessing errMsg loadCurrent(); - events::CreateCollection(name, *dbServerResult); + events::CreateCollection(databaseName, name, *dbServerResult); return Result(tmpRes, *errMsg); } @@ -1877,14 +1879,12 @@ Result ClusterInfo::createCollectionCoordinator( // create collection // This is a best effort, in the worst case the collection stays: ac.sendTransactionWithFailover(transaction); - events::CreateCollection(name, TRI_ERROR_CLUSTER_TIMEOUT); - + events::CreateCollection(databaseName, name, TRI_ERROR_CLUSTER_TIMEOUT); return Result(TRI_ERROR_CLUSTER_TIMEOUT); } if (application_features::ApplicationServer::isStopping()) { - events::CreateCollection(name, TRI_ERROR_SHUTTING_DOWN); - + events::CreateCollection(databaseName, name, TRI_ERROR_SHUTTING_DOWN); return Result(TRI_ERROR_SHUTTING_DOWN); } @@ -1894,6 +1894,7 @@ Result ClusterInfo::createCollectionCoordinator( // create collection } if (!application_features::ApplicationServer::isRetryOK()) { + events::CreateCollection(databaseName, name, TRI_ERROR_CLUSTER_TIMEOUT); return Result(TRI_ERROR_CLUSTER_TIMEOUT); } } @@ -1911,6 +1912,7 @@ Result ClusterInfo::dropCollectionCoordinator( // drop collection double timeout // request timeout ) { if (dbName.empty() || (dbName[0] > '0' && dbName[0] < '9')) { + events::DropCollection(dbName, collectionID, TRI_ERROR_ARANGO_DATABASE_NAME_INVALID); return Result(TRI_ERROR_ARANGO_DATABASE_NAME_INVALID); } @@ -1939,6 +1941,8 @@ Result ClusterInfo::dropCollectionCoordinator( // drop collection errorMsg += "."; + events::DropCollection(dbName, collectionID, + TRI_ERROR_CLUSTER_MUST_NOT_DROP_COLL_OTHER_DISTRIBUTESHARDSLIKE); return Result( // result TRI_ERROR_CLUSTER_MUST_NOT_DROP_COLL_OTHER_DISTRIBUTESHARDSLIKE, // code errorMsg // message @@ -1985,6 +1989,7 @@ Result ClusterInfo::dropCollectionCoordinator( // drop collection LOG_TOPIC("d340d", ERR, Logger::CLUSTER) << "Missing shards information on dropping " << dbName << "/" << collectionID; + events::DropCollection(dbName, collectionID, TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); return Result(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } } @@ -2014,6 +2019,7 @@ Result ClusterInfo::dropCollectionCoordinator( // drop collection LOG_TOPIC("f1bfb", ERR, Logger::CLUSTER) << "Could not get agency dump!"; } + events::DropCollection(dbName, collectionID, TRI_ERROR_CLUSTER_COULD_NOT_DROP_COLLECTION); return Result(TRI_ERROR_CLUSTER_COULD_NOT_DROP_COLLECTION); } @@ -2022,8 +2028,8 @@ Result ClusterInfo::dropCollectionCoordinator( // drop collection if (numberOfShards == 0) { loadCurrent(); - events::DropCollection(collectionID, TRI_ERROR_NO_ERROR); + events::DropCollection(dbName, collectionID, TRI_ERROR_NO_ERROR); return Result(TRI_ERROR_NO_ERROR); } @@ -2036,8 +2042,8 @@ Result ClusterInfo::dropCollectionCoordinator( // drop collection // ...remove the entire directory for the collection ac.removeValues("Current/Collections/" + dbName + "/" + collectionID, true); loadCurrent(); - events::DropCollection(collectionID, *dbServerResult); + events::DropCollection(dbName, collectionID, *dbServerResult); return Result(*dbServerResult); } @@ -2055,14 +2061,14 @@ Result ClusterInfo::dropCollectionCoordinator( // drop collection LOG_TOPIC("37297", ERR, Logger::CLUSTER) << "Could not get agency dump!"; } - events::DropCollection(collectionID, TRI_ERROR_CLUSTER_TIMEOUT); - + events::DropCollection(dbName, collectionID, TRI_ERROR_CLUSTER_TIMEOUT); return Result(TRI_ERROR_CLUSTER_TIMEOUT); } agencyCallback->executeByCallbackOrTimeout(interval); if (!application_features::ApplicationServer::isRetryOK()) { + events::DropCollection(dbName, collectionID, TRI_ERROR_CLUSTER_TIMEOUT); return Result(TRI_ERROR_CLUSTER_TIMEOUT); } } @@ -2136,6 +2142,12 @@ Result ClusterInfo::createViewCoordinator( // create view auto const typeSlice = json.get(arangodb::StaticStrings::DataSourceType); if (!typeSlice.isString()) { + std::string name; + if (json.isObject()) { + name = basics::VelocyPackHelper::getStringValue(json, StaticStrings::DataSourceName, + ""); + } + events::CreateView(databaseName, name, TRI_ERROR_BAD_PARAMETER); return Result(TRI_ERROR_BAD_PARAMETER); } @@ -2144,6 +2156,7 @@ Result ClusterInfo::createViewCoordinator( // create view StaticStrings::Empty); if (name.empty()) { + events::CreateView(databaseName, name, TRI_ERROR_BAD_PARAMETER); return Result(TRI_ERROR_BAD_PARAMETER); // must not be empty } @@ -2158,8 +2171,7 @@ Result ClusterInfo::createViewCoordinator( // create view if (it2 != (*it).second.end()) { // view already exists! - events::CreateView(name, TRI_ERROR_ARANGO_DUPLICATE_NAME); - + events::CreateView(databaseName, name, TRI_ERROR_ARANGO_DUPLICATE_NAME); return Result(TRI_ERROR_ARANGO_DUPLICATE_NAME); } } @@ -2172,8 +2184,7 @@ Result ClusterInfo::createViewCoordinator( // create view if (it2 != (*it).second.end()) { // collection already exists! - events::CreateCollection(name, TRI_ERROR_ARANGO_DUPLICATE_NAME); - + events::CreateCollection(databaseName, name, TRI_ERROR_ARANGO_DUPLICATE_NAME); return Result(TRI_ERROR_ARANGO_DUPLICATE_NAME); } } @@ -2184,14 +2195,12 @@ Result ClusterInfo::createViewCoordinator( // create view // mop: why do these ask the agency instead of checking cluster info? if (!ac.exists("Plan/Databases/" + databaseName)) { - events::CreateView(name, TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); - + events::CreateView(databaseName, name, TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); return Result(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (ac.exists("Plan/Views/" + databaseName + "/" + viewID)) { - events::CreateView(name, TRI_ERROR_CLUSTER_VIEW_ID_EXISTS); - + events::CreateView(databaseName, name, TRI_ERROR_CLUSTER_VIEW_ID_EXISTS); return Result(TRI_ERROR_CLUSTER_VIEW_ID_EXISTS); } @@ -2217,14 +2226,14 @@ Result ClusterInfo::createViewCoordinator( // create view LOG_TOPIC("69f86", ERR, Logger::CLUSTER) << "Could not get agency dump!"; } + events::CreateView(databaseName, name, TRI_ERROR_CLUSTER_COULD_NOT_CREATE_VIEW_IN_PLAN); return Result( // result TRI_ERROR_CLUSTER_COULD_NOT_CREATE_VIEW_IN_PLAN, // code std::string("Precondition that view ") + name + " with ID " + viewID + " does not yet exist failed. Cannot create view."); } - events::CreateView(name, TRI_ERROR_CLUSTER_COULD_NOT_CREATE_VIEW_IN_PLAN); - + events::CreateView(databaseName, name, TRI_ERROR_CLUSTER_COULD_NOT_CREATE_VIEW_IN_PLAN); return Result( // result TRI_ERROR_CLUSTER_COULD_NOT_CREATE_VIEW_IN_PLAN, // code std::string("file: ") + __FILE__ + " line: " + std::to_string(__LINE__) + @@ -2236,6 +2245,7 @@ Result ClusterInfo::createViewCoordinator( // create view // Update our cache: loadPlan(); + events::CreateView(databaseName, name, TRI_ERROR_NO_ERROR); return Result(TRI_ERROR_NO_ERROR); } @@ -2290,7 +2300,7 @@ Result ClusterInfo::dropViewCoordinator( // drop view } } - events::DropView(viewID, result.errorNumber()); + events::DropView(databaseName, viewID, result.errorNumber()); return result; } @@ -2861,7 +2871,7 @@ Result ClusterInfo::dropIndexCoordinator( // drop index AgencyCommResult previous = ac.getValues(planCollKey); if (!previous.successful()) { - events::DropIndex(collectionID, idString, TRI_ERROR_CLUSTER_READING_PLAN_AGENCY); + events::DropIndex(databaseName, collectionID, idString, TRI_ERROR_CLUSTER_READING_PLAN_AGENCY); return Result(TRI_ERROR_CLUSTER_READING_PLAN_AGENCY); } @@ -2869,7 +2879,8 @@ Result ClusterInfo::dropIndexCoordinator( // drop index velocypack::Slice collection = previous.slice()[0].get(std::vector( {AgencyCommManager::path(), "Plan", "Collections", databaseName, collectionID})); if (!collection.isObject()) { - events::DropIndex(collectionID, idString, TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); + events::DropIndex(databaseName, collectionID, idString, + TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); return Result(TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); } @@ -2883,6 +2894,7 @@ Result ClusterInfo::dropIndexCoordinator( // drop index if (!indexes.isArray()) { LOG_TOPIC("63178", DEBUG, Logger::CLUSTER) << "Failed to find index " << databaseName << "/" << collectionID << "/" << iid; + events::DropIndex(databaseName, collectionID, idString, TRI_ERROR_ARANGO_INDEX_NOT_FOUND); return Result(TRI_ERROR_ARANGO_INDEX_NOT_FOUND); } @@ -2900,6 +2912,7 @@ Result ClusterInfo::dropIndexCoordinator( // drop index Index::IndexType type = Index::type(typeSlice.copyString()); if (type == Index::TRI_IDX_TYPE_PRIMARY_INDEX || type == Index::TRI_IDX_TYPE_EDGE_INDEX) { + events::DropIndex(databaseName, collectionID, idString, TRI_ERROR_FORBIDDEN); return Result(TRI_ERROR_FORBIDDEN); } @@ -2912,6 +2925,7 @@ Result ClusterInfo::dropIndexCoordinator( // drop index if (!indexToRemove.isObject()) { LOG_TOPIC("95fe6", DEBUG, Logger::CLUSTER) << "Failed to find index " << databaseName << "/" << collectionID << "/" << iid; + events::DropIndex(databaseName, collectionID, idString, TRI_ERROR_ARANGO_INDEX_NOT_FOUND); return Result(TRI_ERROR_ARANGO_INDEX_NOT_FOUND); } @@ -2978,7 +2992,8 @@ Result ClusterInfo::dropIndexCoordinator( // drop index AgencyCommResult result = ac.sendTransactionWithFailover(trx, 0.0); if (!result.successful()) { - events::DropIndex(collectionID, idString, TRI_ERROR_CLUSTER_COULD_NOT_DROP_INDEX_IN_PLAN); + events::DropIndex(databaseName, collectionID, idString, + TRI_ERROR_CLUSTER_COULD_NOT_DROP_INDEX_IN_PLAN); return Result( // result TRI_ERROR_CLUSTER_COULD_NOT_DROP_INDEX_IN_PLAN, // code @@ -3002,13 +3017,13 @@ Result ClusterInfo::dropIndexCoordinator( // drop index if (*dbServerResult >= 0) { cbGuard.fire(); // unregister cb loadCurrent(); - events::DropIndex(collectionID, idString, *dbServerResult); + events::DropIndex(databaseName, collectionID, idString, *dbServerResult); return Result(*dbServerResult); } if (TRI_microtime() > endTime) { - events::DropIndex(collectionID, idString, TRI_ERROR_CLUSTER_TIMEOUT); + events::DropIndex(databaseName, collectionID, idString, TRI_ERROR_CLUSTER_TIMEOUT); return Result(TRI_ERROR_CLUSTER_TIMEOUT); } diff --git a/arangod/ClusterEngine/ClusterCollection.cpp b/arangod/ClusterEngine/ClusterCollection.cpp index 11070f3cd9..d2599a3a6a 100644 --- a/arangod/ClusterEngine/ClusterCollection.cpp +++ b/arangod/ClusterEngine/ClusterCollection.cpp @@ -387,14 +387,16 @@ bool ClusterCollection::dropIndex(TRI_idx_iid_t iid) { for (std::shared_ptr index : _indexes) { if (iid == index->id()) { _indexes.erase(_indexes.begin() + i); - events::DropIndex("", std::to_string(iid), TRI_ERROR_NO_ERROR); + events::DropIndex(_logicalCollection.vocbase().name(), _logicalCollection.name(), + std::to_string(iid), TRI_ERROR_NO_ERROR); return true; } ++i; } // We tried to remove an index that does not exist - events::DropIndex("", std::to_string(iid), TRI_ERROR_ARANGO_INDEX_NOT_FOUND); + events::DropIndex(_logicalCollection.vocbase().name(), _logicalCollection.name(), + std::to_string(iid), TRI_ERROR_ARANGO_INDEX_NOT_FOUND); return false; } diff --git a/arangod/GeneralServer/GeneralCommTask.cpp b/arangod/GeneralServer/GeneralCommTask.cpp index aa1fc045e0..e1fa328866 100644 --- a/arangod/GeneralServer/GeneralCommTask.cpp +++ b/arangod/GeneralServer/GeneralCommTask.cpp @@ -526,7 +526,7 @@ rest::ResponseCode GeneralCommTask::canAccessPath(GeneralRequest& req) const { VocbaseContext* vc = static_cast(req.requestContext()); TRI_ASSERT(vc != nullptr); if (vc->databaseAuthLevel() == auth::Level::NONE && !StringUtils::isPrefix(path, ApiUser)) { - events::NotAuthorized(&req); + events::NotAuthorized(req); result = rest::ResponseCode::UNAUTHORIZED; LOG_TOPIC("0898a", TRACE, Logger::AUTHORIZATION) << "Access forbidden to " << path; diff --git a/arangod/GeneralServer/HttpCommTask.cpp b/arangod/GeneralServer/HttpCommTask.cpp index 1908de928b..88babb1035 100644 --- a/arangod/GeneralServer/HttpCommTask.cpp +++ b/arangod/GeneralServer/HttpCommTask.cpp @@ -765,7 +765,7 @@ ResponseCode HttpCommTask::handleAuthHeader(HttpRequest* req) { std::string const& authStr = req->header(StaticStrings::Authorization, found); if (!found) { if (_auth->isActive()) { - events::CredentialsMissing(req); + events::CredentialsMissing(*req); return rest::ResponseCode::UNAUTHORIZED; } return rest::ResponseCode::OK; @@ -801,10 +801,10 @@ ResponseCode HttpCommTask::handleAuthHeader(HttpRequest* req) { } if (req->authenticated() || !_auth->isActive()) { - events::Authenticated(req, authMethod); + events::Authenticated(*req, authMethod); return rest::ResponseCode::OK; } else if (_auth->isActive()) { - events::CredentialsBad(req, authMethod); + events::CredentialsBad(*req, authMethod); return rest::ResponseCode::UNAUTHORIZED; } @@ -820,6 +820,6 @@ ResponseCode HttpCommTask::handleAuthHeader(HttpRequest* req) { } } - events::UnknownAuthenticationMethod(req); + events::UnknownAuthenticationMethod(*req); return rest::ResponseCode::UNAUTHORIZED; } diff --git a/arangod/GeneralServer/RestHandler.cpp b/arangod/GeneralServer/RestHandler.cpp index 2639c83ba4..cef3982f29 100644 --- a/arangod/GeneralServer/RestHandler.cpp +++ b/arangod/GeneralServer/RestHandler.cpp @@ -53,8 +53,8 @@ RestHandler::RestHandler(GeneralRequest* request, GeneralResponse* response) _request(request), _response(response), _statistics(nullptr), - _handlerId(0), - _state(HandlerState::PREPARE) {} + _state(HandlerState::PREPARE), + _handlerId(0) {} RestHandler::~RestHandler() { RequestStatistics* stat = _statistics.exchange(nullptr); diff --git a/arangod/GeneralServer/RestHandler.h b/arangod/GeneralServer/RestHandler.h index 88d3451c0c..dcc4058fe0 100644 --- a/arangod/GeneralServer/RestHandler.h +++ b/arangod/GeneralServer/RestHandler.h @@ -138,16 +138,6 @@ class RestHandler : public std::enable_shared_from_this { void generateError(arangodb::Result const&); private: - enum class HandlerState { - PREPARE, - EXECUTE, - PAUSED, - CONTINUED, - FINALIZE, - DONE, - FAILED - }; - void runHandlerStateMachine(); void prepareEngine(); @@ -159,17 +149,27 @@ class RestHandler : public std::enable_shared_from_this { void shutdownEngine(); protected: + enum class HandlerState { + PREPARE, + EXECUTE, + PAUSED, + CONTINUED, + FINALIZE, + DONE, + FAILED + }; + std::atomic _canceled; std::unique_ptr _request; std::unique_ptr _response; std::atomic _statistics; + HandlerState _state; private: uint64_t _handlerId; - HandlerState _state; std::function _callback; mutable Mutex _executionMutex; diff --git a/arangod/IResearch/IResearchView.cpp b/arangod/IResearch/IResearchView.cpp index 5066cb9e03..4e01fd192f 100644 --- a/arangod/IResearch/IResearchView.cpp +++ b/arangod/IResearch/IResearchView.cpp @@ -31,6 +31,7 @@ #include "Aql/PlanCache.h" #include "Aql/QueryCache.h" #include "Basics/StaticStrings.h" +#include "Basics/VelocyPackHelper.h" #include "RestServer/DatabaseFeature.h" #include "RestServer/ViewTypesFeature.h" #include "StorageEngine/EngineSelectorFeature.h" @@ -39,6 +40,7 @@ #include "StorageEngine/TransactionState.h" #include "Transaction/Methods.h" #include "Transaction/StandaloneContext.h" +#include "Utils/Events.h" #include "Utils/ExecContext.h" #include "VocBase/LogicalCollection.h" @@ -146,6 +148,12 @@ struct IResearchView::ViewFactory : public arangodb::ViewFactory { : IResearchLinkHelper::validateLinks(vocbase, links); if (!res.ok()) { + std::string name; + if (definition.isObject()) { + name = arangodb::basics::VelocyPackHelper::getStringValue( + definition, arangodb::StaticStrings::DataSourceName, ""); + } + events::CreateView(vocbase.name(), name, res.errorNumber()); return res; } @@ -156,10 +164,22 @@ struct IResearchView::ViewFactory : public arangodb::ViewFactory { : arangodb::LogicalViewHelperClusterInfo::construct(impl, vocbase, definition); if (!res.ok()) { + std::string name; + if (definition.isObject()) { + name = arangodb::basics::VelocyPackHelper::getStringValue( + definition, arangodb::StaticStrings::DataSourceName, ""); + } + events::CreateView(vocbase.name(), name, res.errorNumber()); return res; } if (!impl) { + std::string name; + if (definition.isObject()) { + name = arangodb::basics::VelocyPackHelper::getStringValue( + definition, arangodb::StaticStrings::DataSourceName, ""); + } + events::CreateView(vocbase.name(), name, TRI_ERROR_INTERNAL); return arangodb::Result(TRI_ERROR_INTERNAL, std::string( "failure during instantiation while creating " @@ -167,7 +187,7 @@ struct IResearchView::ViewFactory : public arangodb::ViewFactory { vocbase.name() + "'"); } - // create links on a best-effor basis + // create links on a best-effort basis // link creation failure does not cause view creation failure try { std::unordered_set collections; @@ -180,14 +200,34 @@ struct IResearchView::ViewFactory : public arangodb::ViewFactory { } } catch (arangodb::basics::Exception const& e) { IR_LOG_EXCEPTION(); + std::string name; + if (definition.isObject()) { + name = arangodb::basics::VelocyPackHelper::getStringValue( + definition, arangodb::StaticStrings::DataSourceName, ""); + } + events::CreateView(vocbase.name(), name, e.code()); LOG_TOPIC("eddb2", WARN, arangodb::iresearch::TOPIC) - << "caught exception while creating links while creating arangosearch view '" << impl->name() << "': " << e.code() << " " << e.what(); + << "caught exception while creating links while creating " + "arangosearch view '" + << impl->name() << "': " << e.code() << " " << e.what(); } catch (std::exception const& e) { IR_LOG_EXCEPTION(); + std::string name; + if (definition.isObject()) { + name = arangodb::basics::VelocyPackHelper::getStringValue( + definition, arangodb::StaticStrings::DataSourceName, ""); + } + events::CreateView(vocbase.name(), name, TRI_ERROR_INTERNAL); LOG_TOPIC("dc829", WARN, arangodb::iresearch::TOPIC) << "caught exception while creating links while creating arangosearch view '" << impl->name() << "': " << e.what(); } catch (...) { IR_LOG_EXCEPTION(); + std::string name; + if (definition.isObject()) { + name = arangodb::basics::VelocyPackHelper::getStringValue( + definition, arangodb::StaticStrings::DataSourceName, ""); + } + events::CreateView(vocbase.name(), name, TRI_ERROR_INTERNAL); LOG_TOPIC("6491c", WARN, arangodb::iresearch::TOPIC) << "caught exception while creating links while creating arangosearch view '" << impl->name() << "'"; } diff --git a/arangod/MMFiles/MMFilesCollection.cpp b/arangod/MMFiles/MMFilesCollection.cpp index ad494c998a..b458892a29 100644 --- a/arangod/MMFiles/MMFilesCollection.cpp +++ b/arangod/MMFiles/MMFilesCollection.cpp @@ -2263,7 +2263,7 @@ std::shared_ptr MMFilesCollection::createIndex(transaction::Methods& trx, } #if USE_PLAN_CACHE - arangodb::aql::PlanCache::instance()->invalidate(_logicalCollection->vocbase()); + arangodb::aql::PlanCache::instance()->invalidate(_logicalCollection.vocbase()); #endif // Until here no harm is done if sth fails. The shared ptr will clean up. if // left before @@ -2386,7 +2386,8 @@ void MMFilesCollection::addIndexLocal(std::shared_ptr idx) { bool MMFilesCollection::dropIndex(TRI_idx_iid_t iid) { if (iid == 0) { // invalid index id or primary index - events::DropIndex("", std::to_string(iid), TRI_ERROR_NO_ERROR); + events::DropIndex(_logicalCollection.vocbase().name(), _logicalCollection.name(), + std::to_string(iid), TRI_ERROR_NO_ERROR); return true; } @@ -2394,7 +2395,8 @@ bool MMFilesCollection::dropIndex(TRI_idx_iid_t iid) { if (!removeIndex(iid)) { // We tried to remove an index that does not exist - events::DropIndex("", std::to_string(iid), TRI_ERROR_ARANGO_INDEX_NOT_FOUND); + events::DropIndex(_logicalCollection.vocbase().name(), _logicalCollection.name(), + std::to_string(iid), TRI_ERROR_ARANGO_INDEX_NOT_FOUND); return false; } @@ -2421,11 +2423,13 @@ bool MMFilesCollection::dropIndex(TRI_idx_iid_t iid) { engine->dropIndexWalMarker(&vocbase, cid, markerBuilder.slice(), true, res); if (res == TRI_ERROR_NO_ERROR) { - events::DropIndex("", std::to_string(iid), TRI_ERROR_NO_ERROR); + events::DropIndex(_logicalCollection.vocbase().name(), _logicalCollection.name(), + std::to_string(iid), TRI_ERROR_NO_ERROR); } else { LOG_TOPIC("96677", WARN, arangodb::Logger::ENGINES) << "could not save index drop marker in log: " << TRI_errno_string(res); - events::DropIndex("", std::to_string(iid), res); + events::DropIndex(_logicalCollection.vocbase().name(), + _logicalCollection.name(), std::to_string(iid), res); } } return true; diff --git a/arangod/RestHandler/RestAuthHandler.cpp b/arangod/RestHandler/RestAuthHandler.cpp index cd8ca3b84f..95476505a5 100644 --- a/arangod/RestHandler/RestAuthHandler.cpp +++ b/arangod/RestHandler/RestAuthHandler.cpp @@ -31,6 +31,7 @@ #include "Logger/Logger.h" #include "Rest/HttpRequest.h" #include "Ssl/SslInterface.h" +#include "Utils/Events.h" using namespace arangodb; using namespace arangodb::basics; @@ -111,3 +112,15 @@ RestStatus RestAuthHandler::badRequest() { "invalid JSON"); return RestStatus::DONE; } + +void RestAuthHandler::shutdownExecute(bool isFinalized) noexcept { + try { + if (_isValid) { + events::LoggedIn(*_request, _username); + } else { + events::CredentialsBad(*_request, _username); + } + } catch (...) { + } + RestVocbaseBaseHandler::shutdownExecute(isFinalized); +} diff --git a/arangod/RestHandler/RestAuthHandler.h b/arangod/RestHandler/RestAuthHandler.h index cf78412568..34575e6e71 100644 --- a/arangod/RestHandler/RestAuthHandler.h +++ b/arangod/RestHandler/RestAuthHandler.h @@ -40,10 +40,7 @@ class RestAuthHandler : public RestVocbaseBaseHandler { char const* name() const override final { return "RestAuthHandler"; } RequestLane lane() const override final { return RequestLane::CLIENT_SLOW; } RestStatus execute() override; - -#ifdef USE_ENTERPRISE void shutdownExecute(bool isFinalized) noexcept override; -#endif private: RestStatus badRequest(); diff --git a/arangod/RestHandler/RestCollectionHandler.cpp b/arangod/RestHandler/RestCollectionHandler.cpp index 27ba976d97..ed5577a0c5 100644 --- a/arangod/RestHandler/RestCollectionHandler.cpp +++ b/arangod/RestHandler/RestCollectionHandler.cpp @@ -33,6 +33,7 @@ #include "StorageEngine/StorageEngine.h" #include "Transaction/Methods.h" #include "Transaction/StandaloneContext.h" +#include "Utils/Events.h" #include "Utils/OperationOptions.h" #include "Utils/SingleCollectionTransaction.h" #include "VocBase/LogicalCollection.h" @@ -267,6 +268,7 @@ void RestCollectionHandler::handleCommandPost() { VPackSlice const body = this->parseVPackBody(parseSuccess); if (!parseSuccess) { // error message generated in parseVPackBody + events::CreateCollection(_vocbase.name(), "", TRI_ERROR_BAD_PARAMETER); return; } @@ -274,6 +276,7 @@ void RestCollectionHandler::handleCommandPost() { if (!body.isObject() || !(nameSlice = body.get("name")).isString() || nameSlice.getStringLength() == 0) { generateError(rest::ResponseCode::BAD, TRI_ERROR_ARANGO_ILLEGAL_NAME); + events::CreateCollection(_vocbase.name(), "", TRI_ERROR_ARANGO_ILLEGAL_NAME); return; } @@ -505,6 +508,7 @@ void RestCollectionHandler::handleCommandDelete() { if (suffixes.size() != 1) { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "expected DELETE /_api/collection/"); + events::DropCollection(_vocbase.name(), "", TRI_ERROR_HTTP_BAD_PARAMETER); return; } @@ -526,6 +530,7 @@ void RestCollectionHandler::handleCommandDelete() { }); if (found.fail()) { + events::DropCollection(_vocbase.name(), name, found.errorNumber()); generateError(found); } else if (res.fail()) { generateError(res); diff --git a/arangod/RestHandler/RestCursorHandler.cpp b/arangod/RestHandler/RestCursorHandler.cpp index 1d8cc28ed6..a0a1b82685 100644 --- a/arangod/RestHandler/RestCursorHandler.cpp +++ b/arangod/RestHandler/RestCursorHandler.cpp @@ -21,6 +21,7 @@ /// @author Jan Steemann //////////////////////////////////////////////////////////////////////////////// +#include "RestCursorHandler.h" #include "Aql/Query.h" #include "Aql/QueryRegistry.h" #include "Basics/Exceptions.h" @@ -28,10 +29,10 @@ #include "Basics/StaticStrings.h" #include "Basics/VelocyPackHelper.h" #include "Cluster/ServerState.h" -#include "RestCursorHandler.h" #include "Transaction/Context.h" #include "Utils/Cursor.h" #include "Utils/CursorRepository.h" +#include "Utils/Events.h" #include #include @@ -50,7 +51,8 @@ RestCursorHandler::RestCursorHandler(GeneralRequest* request, GeneralResponse* r _leasedCursor(nullptr), _hasStarted(false), _queryKilled(false), - _isValidForFinalize(false) {} + _isValidForFinalize(false), + _auditLogged(false) {} RestCursorHandler::~RestCursorHandler() { if (_leasedCursor) { @@ -102,11 +104,57 @@ RestStatus RestCursorHandler::continueExecute() { return RestStatus::DONE; } +void RestCursorHandler::shutdownExecute(bool isFinalized) noexcept { + TRI_DEFER(RestVocbaseBaseHandler::shutdownExecute(isFinalized)); + auto const type = _request->requestType(); + + // request not done yet + if (_state == HandlerState::PAUSED) { + return; + } + + // only trace create cursor requests + if (type != rest::RequestType::POST) { + return; + } + + if (!_isValidForFinalize || _auditLogged) { + // set by RestCursorHandler before + return; + } + + try { + bool parseSuccess = true; + std::shared_ptr parsedBody = parseVelocyPackBody(parseSuccess); + VPackSlice body = parsedBody.get()->slice(); + events::QueryDocument(*_request, _response.get(), body); + _auditLogged = true; + } catch (...) { + } +} + bool RestCursorHandler::cancel() { RestVocbaseBaseHandler::cancel(); return cancelQuery(); } +void RestCursorHandler::handleError(basics::Exception const& ex) { + TRI_DEFER(RestVocbaseBaseHandler::handleError(ex)); + + if (!_isValidForFinalize || _auditLogged) { + return; + } + + try { + bool parseSuccess = true; + std::shared_ptr parsedBody = parseVelocyPackBody(parseSuccess); + VPackSlice body = parsedBody.get()->slice(); + events::QueryDocument(*_request, _response.get(), body); + _auditLogged = true; + } catch (...) { + } +} + //////////////////////////////////////////////////////////////////////////////// /// @brief register the query either as streaming cursor or in _query /// the query is not executed here. diff --git a/arangod/RestHandler/RestCursorHandler.h b/arangod/RestHandler/RestCursorHandler.h index 565a19f601..b28a03b94a 100644 --- a/arangod/RestHandler/RestCursorHandler.h +++ b/arangod/RestHandler/RestCursorHandler.h @@ -64,12 +64,10 @@ class RestCursorHandler : public RestVocbaseBaseHandler { RequestLane lane() const override final { return RequestLane::CLIENT_AQL; } virtual RestStatus continueExecute() override; - -#ifdef USE_ENTERPRISE void shutdownExecute(bool isFinalized) noexcept override; -#endif bool cancel() override final; + void handleError(basics::Exception const&) override; protected: ////////////////////////////////////////////////////////////////////////////// @@ -208,6 +206,9 @@ class RestCursorHandler : public RestVocbaseBaseHandler { bool _isValidForFinalize; + /// @brief whether or not an audit message has already been logged + bool _auditLogged; + ////////////////////////////////////////////////////////////////////////////// /// @brief A shared pointer to the query options velocypack, s.t. we avoid /// to reparse and set default options diff --git a/arangod/RestHandler/RestDatabaseHandler.cpp b/arangod/RestHandler/RestDatabaseHandler.cpp index 4f970e11a3..d3edb280f5 100644 --- a/arangod/RestHandler/RestDatabaseHandler.cpp +++ b/arangod/RestHandler/RestDatabaseHandler.cpp @@ -24,6 +24,7 @@ #include "ApplicationFeatures/ApplicationServer.h" #include "Rest/HttpRequest.h" +#include "Utils/Events.h" #include "VocBase/Methods/Databases.h" #include @@ -107,7 +108,7 @@ RestStatus RestDatabaseHandler::createDatabase() { if (!_vocbase.isSystem()) { generateError(GeneralResponse::responseCode(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE), TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); - + events::CreateDatabase("", TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); return RestStatus::DONE; } @@ -116,11 +117,13 @@ RestStatus RestDatabaseHandler::createDatabase() { VPackSlice body = this->parseVPackBody(parseSuccess); if (!suffixes.empty() || !parseSuccess) { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER); + events::CreateDatabase("", TRI_ERROR_BAD_PARAMETER); return RestStatus::DONE; } VPackSlice nameVal = body.get("name"); if (!nameVal.isString()) { generateError(rest::ResponseCode::BAD, TRI_ERROR_ARANGO_DATABASE_NAME_INVALID); + events::CreateDatabase("", TRI_ERROR_ARANGO_DATABASE_NAME_INVALID); return RestStatus::DONE; } std::string dbName = nameVal.copyString(); @@ -149,12 +152,13 @@ RestStatus RestDatabaseHandler::deleteDatabase() { if (!_vocbase.isSystem()) { generateError(GeneralResponse::responseCode(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE), TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); - + events::DropDatabase("", TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); return RestStatus::DONE; } std::vector const& suffixes = _request->suffixes(); if (suffixes.size() != 1) { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER); + events::DropDatabase("", TRI_ERROR_HTTP_BAD_PARAMETER); return RestStatus::DONE; } diff --git a/arangod/RestHandler/RestDocumentHandler.cpp b/arangod/RestHandler/RestDocumentHandler.cpp index 5d235ed521..b2e1b00130 100644 --- a/arangod/RestHandler/RestDocumentHandler.cpp +++ b/arangod/RestHandler/RestDocumentHandler.cpp @@ -21,15 +21,16 @@ /// @author Dr. Frank Celler //////////////////////////////////////////////////////////////////////////////// +#include "RestDocumentHandler.h" #include "Basics/StaticStrings.h" #include "Basics/StringUtils.h" #include "Basics/VelocyPackHelper.h" #include "Cluster/ServerState.h" #include "Rest/HttpRequest.h" -#include "RestDocumentHandler.h" #include "Transaction/Helpers.h" #include "Transaction/Hints.h" #include "Transaction/StandaloneContext.h" +#include "Utils/Events.h" #include "Utils/OperationOptions.h" #include "Utils/SingleCollectionTransaction.h" #include "VocBase/vocbase.h" @@ -74,6 +75,29 @@ RestStatus RestDocumentHandler::execute() { return RestStatus::DONE; } +void RestDocumentHandler::shutdownExecute(bool isFinalized) noexcept { + try { + GeneralRequest const* request = _request.get(); + auto const type = request->requestType(); + int result = static_cast(_response->responseCode()); + + switch (type) { + case rest::RequestType::DELETE_REQ: + case rest::RequestType::GET: + case rest::RequestType::HEAD: + case rest::RequestType::POST: + case rest::RequestType::PUT: + case rest::RequestType::PATCH: + break; + default: + events::IllegalDocumentOperation(*request, result); + break; + } + } catch (...) { + } + RestVocbaseBaseHandler::shutdownExecute(isFinalized); +} + /// @brief returns the short id of the server which should handle this request uint32_t RestDocumentHandler::forwardingTarget() { if (!ServerState::instance()->isCoordinator()) { diff --git a/arangod/RestHandler/RestDocumentHandler.h b/arangod/RestHandler/RestDocumentHandler.h index 0b0c4b6b19..f78ccf2274 100644 --- a/arangod/RestHandler/RestDocumentHandler.h +++ b/arangod/RestHandler/RestDocumentHandler.h @@ -36,10 +36,7 @@ class RestDocumentHandler : public RestVocbaseBaseHandler { RestStatus execute() override final; char const* name() const override final { return "RestDocumentHandler"; } RequestLane lane() const override final { return RequestLane::CLIENT_SLOW; } - -#ifdef USE_ENTERPRISE void shutdownExecute(bool isFinalized) noexcept override; -#endif protected: virtual uint32_t forwardingTarget() override; diff --git a/arangod/RestHandler/RestIndexHandler.cpp b/arangod/RestHandler/RestIndexHandler.cpp index d8c024d5f0..27d5a9236d 100644 --- a/arangod/RestHandler/RestIndexHandler.cpp +++ b/arangod/RestHandler/RestIndexHandler.cpp @@ -26,9 +26,10 @@ #include "Cluster/ClusterInfo.h" #include "Cluster/ServerState.h" #include "Rest/HttpRequest.h" +#include "Utils/Events.h" #include "Utils/SingleCollectionTransaction.h" -#include "VocBase/Methods/Indexes.h" #include "VocBase/LogicalCollection.h" +#include "VocBase/Methods/Indexes.h" #include #include @@ -212,6 +213,7 @@ RestStatus RestIndexHandler::createIndex() { return RestStatus::DONE; } if (!suffixes.empty()) { + events::CreateIndex(_vocbase.name(), "(unknown)", body, TRI_ERROR_BAD_PARAMETER); generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "expecting POST " + _request->requestPath() + "?collection="); @@ -221,12 +223,15 @@ RestStatus RestIndexHandler::createIndex() { bool found = false; std::string cName = _request->value("collection", found); if (!found) { + events::CreateIndex(_vocbase.name(), "(unknown)", body, + TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); return RestStatus::DONE; } auto coll = collection(cName); if (coll == nullptr) { + events::CreateIndex(_vocbase.name(), cName, body, TRI_ERROR_ARANGO_INDEX_NOT_FOUND); generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); return RestStatus::DONE; } @@ -272,6 +277,7 @@ RestStatus RestIndexHandler::createIndex() { RestStatus RestIndexHandler::dropIndex() { std::vector const& suffixes = _request->suffixes(); if (suffixes.size() != 2) { + events::DropIndex(_vocbase.name(), "(unknown)", "(unknown)", TRI_ERROR_HTTP_BAD_PARAMETER); generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "expecting DELETE //"); return RestStatus::DONE; @@ -280,6 +286,7 @@ RestStatus RestIndexHandler::dropIndex() { std::string const& cName = suffixes[0]; auto coll = collection(cName); if (coll == nullptr) { + events::DropIndex(_vocbase.name(), cName, "(unknown)", TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); return RestStatus::DONE; } diff --git a/arangod/RestHandler/RestViewHandler.cpp b/arangod/RestHandler/RestViewHandler.cpp index 2364096baa..6942fd4c7c 100644 --- a/arangod/RestHandler/RestViewHandler.cpp +++ b/arangod/RestHandler/RestViewHandler.cpp @@ -30,6 +30,7 @@ #include "Rest/GeneralResponse.h" #include "RestServer/DatabaseFeature.h" #include "Utils/CollectionNameResolver.h" +#include "Utils/Events.h" #include "VocBase/LogicalView.h" #include @@ -140,7 +141,7 @@ RestStatus RestViewHandler::execute() { void RestViewHandler::createView() { if (_request->payload().isEmptyObject()) { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON); - + events::CreateView(_vocbase.name(), "", TRI_ERROR_HTTP_CORRUPTED_JSON); return; } @@ -149,7 +150,7 @@ void RestViewHandler::createView() { if (!suffixes.empty()) { generateError(rest::ResponseCode::BAD, TRI_ERROR_BAD_PARAMETER, "expecting POST /_api/view"); - + events::CreateView(_vocbase.name(), "", TRI_ERROR_BAD_PARAMETER); return; } @@ -157,6 +158,7 @@ void RestViewHandler::createView() { VPackSlice const body = this->parseVPackBody(parseSuccess); if (!parseSuccess) { + events::CreateView(_vocbase.name(), "", TRI_ERROR_BAD_PARAMETER); return; } @@ -168,7 +170,7 @@ void RestViewHandler::createView() { if (!body.isObject()) { badParamError(); - + events::CreateView(_vocbase.name(), "", TRI_ERROR_BAD_PARAMETER); return; } @@ -177,7 +179,9 @@ void RestViewHandler::createView() { if (!nameSlice.isString() || !typeSlice.isString()) { badParamError(); - + events::CreateView(_vocbase.name(), + (nameSlice.isString() ? nameSlice.copyString() : ""), + TRI_ERROR_BAD_PARAMETER); return; } @@ -188,7 +192,7 @@ void RestViewHandler::createView() { if (!canUse(auth::Level::RW, _vocbase)) { generateError( Result(TRI_ERROR_FORBIDDEN, "insufficient rights to create view")); - + events::CreateView(_vocbase.name(), nameSlice.copyString(), TRI_ERROR_FORBIDDEN); return; } @@ -198,14 +202,14 @@ void RestViewHandler::createView() { if (!res.ok()) { generateError(res); - + events::CreateView(_vocbase.name(), nameSlice.copyString(), res.errorNumber()); return; } if (!view) { generateError( arangodb::Result(TRI_ERROR_INTERNAL, "problem creating view")); - + events::CreateView(_vocbase.name(), nameSlice.copyString(), TRI_ERROR_INTERNAL); return; } @@ -216,15 +220,16 @@ void RestViewHandler::createView() { if (!res.ok()) { generateError(res); - return; } builder.close(); generateResult(rest::ResponseCode::CREATED, builder.slice()); } catch (basics::Exception const& ex) { + events::CreateView(_vocbase.name(), nameSlice.copyString(), ex.code()); generateError(arangodb::Result(ex.code(), ex.message())); } catch (...) { + events::CreateView(_vocbase.name(), nameSlice.copyString(), TRI_errno()); generateError(arangodb::Result(TRI_errno(), "problem creating view")); } } @@ -389,6 +394,7 @@ void RestViewHandler::deleteView() { generateError(rest::ResponseCode::BAD, TRI_ERROR_BAD_PARAMETER, "expecting DELETE /_api/view/"); + events::DropView(_vocbase.name(), "", TRI_ERROR_BAD_PARAMETER); return; } @@ -399,6 +405,7 @@ void RestViewHandler::deleteView() { if (!view) { generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); + events::DropView(_vocbase.name(), name, TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); return; } @@ -410,6 +417,7 @@ void RestViewHandler::deleteView() { generateError( Result(TRI_ERROR_FORBIDDEN, "insufficient rights to drop view")); + events::DropView(_vocbase.name(), name, TRI_ERROR_FORBIDDEN); return; } @@ -418,6 +426,7 @@ void RestViewHandler::deleteView() { generateError( Result(TRI_ERROR_FORBIDDEN, "insufficient rights to drop system view")); + events::DropView(_vocbase.name(), name, TRI_ERROR_FORBIDDEN); return; } diff --git a/arangod/RestServer/DatabaseFeature.cpp b/arangod/RestServer/DatabaseFeature.cpp index f301ea9a9f..cccabba376 100644 --- a/arangod/RestServer/DatabaseFeature.cpp +++ b/arangod/RestServer/DatabaseFeature.cpp @@ -630,11 +630,13 @@ int DatabaseFeature::createDatabase(TRI_voc_tick_t id, std::string const& name, LOG_TOPIC("e7444", ERR, arangodb::Logger::FIXME) << "initializing replication applier for database '" << vocbase->name() << "' failed: " << ex.what(); + events::CreateDatabase(name, ex.code()); return ex.code(); } catch (std::exception const& ex) { LOG_TOPIC("56c41", ERR, arangodb::Logger::FIXME) << "initializing replication applier for database '" << vocbase->name() << "' failed: " << ex.what(); + events::CreateDatabase(name, TRI_ERROR_INTERNAL); return TRI_ERROR_INTERNAL; } @@ -651,6 +653,7 @@ int DatabaseFeature::createDatabase(TRI_voc_tick_t id, std::string const& name, int res = createApplicationDirectory(name, appPath); if (res != TRI_ERROR_NO_ERROR) { + events::CreateDatabase(name, res); THROW_ARANGO_EXCEPTION(res); } } @@ -715,6 +718,7 @@ int DatabaseFeature::dropDatabase(std::string const& name, bool waitForDeletion, bool removeAppsDirectory) { if (name == TRI_VOC_SYSTEM_DATABASE) { // prevent deletion of system database + events::DropDatabase(name, TRI_ERROR_FORBIDDEN); return TRI_ERROR_FORBIDDEN; } @@ -769,6 +773,7 @@ int DatabaseFeature::dropDatabase(std::string const& name, bool waitForDeletion, vocbase->visitDataSources(visitor, true); // aquire a write lock to avoid potential deadlocks if (TRI_ERROR_NO_ERROR != res) { + events::DropDatabase(name, res); return res; } @@ -776,6 +781,7 @@ int DatabaseFeature::dropDatabase(std::string const& name, bool waitForDeletion, newLists->_droppedDatabases.insert(vocbase); } catch (...) { delete newLists; + events::DropDatabase(name, TRI_ERROR_OUT_OF_MEMORY); return TRI_ERROR_OUT_OF_MEMORY; } @@ -838,6 +844,7 @@ int DatabaseFeature::dropDatabase(TRI_voc_tick_t id, bool waitForDeletion, } if (name.empty()) { + events::DropDatabase(name, TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); return TRI_ERROR_ARANGO_DATABASE_NOT_FOUND; } // and call the regular drop function diff --git a/arangod/RestServer/ViewTypesFeature.cpp b/arangod/RestServer/ViewTypesFeature.cpp index d5a23f9ccb..e3b783f5a2 100644 --- a/arangod/RestServer/ViewTypesFeature.cpp +++ b/arangod/RestServer/ViewTypesFeature.cpp @@ -23,15 +23,24 @@ #include "ViewTypesFeature.h" #include "ApplicationFeatures/ApplicationServer.h" -#include "BootstrapFeature.h" +#include "Basics/StaticStrings.h" +#include "Basics/VelocyPackHelper.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" +#include "RestServer/BootstrapFeature.h" +#include "Utils/Events.h" namespace { struct InvalidViewFactory : public arangodb::ViewFactory { - virtual arangodb::Result create(arangodb::LogicalView::ptr&, TRI_vocbase_t&, + virtual arangodb::Result create(arangodb::LogicalView::ptr&, TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition) const override { + std::string name; + if (definition.isObject()) { + name = arangodb::basics::VelocyPackHelper::getStringValue( + definition, arangodb::StaticStrings::DataSourceName, ""); + } + arangodb::events::CreateView(vocbase.name(), name, TRI_ERROR_INTERNAL); return arangodb::Result( TRI_ERROR_BAD_PARAMETER, std::string( @@ -106,4 +115,4 @@ void ViewTypesFeature::unprepare() { _factories.clear(); } // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- \ No newline at end of file +// ----------------------------------------------------------------------------- diff --git a/arangod/RocksDBEngine/RocksDBCollection.cpp b/arangod/RocksDBEngine/RocksDBCollection.cpp index 9c10e1e260..2e121caee3 100644 --- a/arangod/RocksDBEngine/RocksDBCollection.cpp +++ b/arangod/RocksDBEngine/RocksDBCollection.cpp @@ -450,7 +450,7 @@ std::shared_ptr RocksDBCollection::createIndex(VPackSlice const& info, } guard.unlock(); #if USE_PLAN_CACHE - arangodb::aql::PlanCache::instance()->invalidate(_logicalCollection->vocbase()); + arangodb::aql::PlanCache::instance()->invalidate(_logicalCollection.vocbase()); #endif // inBackground index might not recover selectivity estimate w/o sync @@ -516,7 +516,8 @@ bool RocksDBCollection::dropIndex(TRI_idx_iid_t iid) { if (!toRemove) { // index not found // We tried to remove an index that does not exist - events::DropIndex("", std::to_string(iid), TRI_ERROR_ARANGO_INDEX_NOT_FOUND); + events::DropIndex(_logicalCollection.vocbase().name(), _logicalCollection.name(), + std::to_string(iid), TRI_ERROR_ARANGO_INDEX_NOT_FOUND); return false; } @@ -531,7 +532,8 @@ bool RocksDBCollection::dropIndex(TRI_idx_iid_t iid) { return false; } - events::DropIndex("", std::to_string(iid), TRI_ERROR_NO_ERROR); + events::DropIndex(_logicalCollection.vocbase().name(), _logicalCollection.name(), + std::to_string(iid), TRI_ERROR_NO_ERROR); cindex->compact(); // trigger compaction before deleting the object @@ -1320,7 +1322,7 @@ Result RocksDBCollection::removeDocument(arangodb::transaction::Methods* trx, /*LOG_TOPIC("17502", ERR, Logger::ENGINES) << "Delete rev: " << revisionId << " trx: " << trx->state()->id() << " seq: " << mthds->sequenceNumber() - << " objectID " << _objectId << " name: " << _logicalCollection->name();*/ + << " objectID " << _objectId << " name: " << _logicalCollection.name();*/ READ_LOCKER(guard, _indexesLock); for (std::shared_ptr const& idx : _indexes) { diff --git a/arangod/Transaction/Methods.cpp b/arangod/Transaction/Methods.cpp index 3057a87486..5a46d7ccd1 100644 --- a/arangod/Transaction/Methods.cpp +++ b/arangod/Transaction/Methods.cpp @@ -1542,14 +1542,20 @@ OperationResult transaction::Methods::document(std::string const& collectionName if (!value.isObject() && !value.isArray()) { // must provide a document object or an array of documents + events::ReadDocument(vocbase().name(), collectionName, value, + TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } + OperationResult result; if (_state->isCoordinator()) { - return documentCoordinator(collectionName, value, options); + result = documentCoordinator(collectionName, value, options); + } else { + result = documentLocal(collectionName, value, options); } - return documentLocal(collectionName, value, options); + events::ReadDocument(vocbase().name(), collectionName, value, result.errorNumber()); + return result; } /// @brief read one or multiple documents in a collection, coordinator @@ -1668,20 +1674,29 @@ OperationResult transaction::Methods::insert(std::string const& collectionName, if (!value.isObject() && !value.isArray()) { // must provide a document object or an array of documents + events::CreateDocument(vocbase().name(), collectionName, value, + TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } if (value.isArray() && value.length() == 0) { + events::CreateDocument(vocbase().name(), collectionName, value, TRI_ERROR_NO_ERROR); return emptyResult(options); } // Validate Edges OperationOptions optionsCopy = options; + OperationResult result; if (_state->isCoordinator()) { - return insertCoordinator(collectionName, value, optionsCopy); + result = insertCoordinator(collectionName, value, optionsCopy); + } else { + result = insertLocal(collectionName, value, optionsCopy); } - return insertLocal(collectionName, value, optionsCopy); + events::CreateDocument(vocbase().name(), collectionName, + ((result.ok() && options.returnNew) ? result.slice() : value), + result.errorNumber()); + return result; } /// @brief create one or multiple documents in a collection, coordinator @@ -1958,19 +1973,26 @@ OperationResult transaction::Methods::update(std::string const& collectionName, if (!newValue.isObject() && !newValue.isArray()) { // must provide a document object or an array of documents + events::ModifyDocument(vocbase().name(), collectionName, newValue, + TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } if (newValue.isArray() && newValue.length() == 0) { + events::ModifyDocument(vocbase().name(), collectionName, newValue, TRI_ERROR_NO_ERROR); return emptyResult(options); } OperationOptions optionsCopy = options; + OperationResult result; if (_state->isCoordinator()) { - return updateCoordinator(collectionName, newValue, optionsCopy); + result = updateCoordinator(collectionName, newValue, optionsCopy); + } else { + result = modifyLocal(collectionName, newValue, optionsCopy, TRI_VOC_DOCUMENT_OPERATION_UPDATE); } - return modifyLocal(collectionName, newValue, optionsCopy, TRI_VOC_DOCUMENT_OPERATION_UPDATE); + events::ModifyDocument(vocbase().name(), collectionName, newValue, result.errorNumber()); + return result; } /// @brief update one or multiple documents in a collection, coordinator @@ -2006,19 +2028,27 @@ OperationResult transaction::Methods::replace(std::string const& collectionName, if (!newValue.isObject() && !newValue.isArray()) { // must provide a document object or an array of documents + events::ReplaceDocument(vocbase().name(), collectionName, newValue, + TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } if (newValue.isArray() && newValue.length() == 0) { + events::ReplaceDocument(vocbase().name(), collectionName, newValue, TRI_ERROR_NO_ERROR); return emptyResult(options); } OperationOptions optionsCopy = options; + OperationResult result; if (_state->isCoordinator()) { - return replaceCoordinator(collectionName, newValue, optionsCopy); + result = replaceCoordinator(collectionName, newValue, optionsCopy); + } else { + result = modifyLocal(collectionName, newValue, optionsCopy, + TRI_VOC_DOCUMENT_OPERATION_REPLACE); } - return modifyLocal(collectionName, newValue, optionsCopy, TRI_VOC_DOCUMENT_OPERATION_REPLACE); + events::ReplaceDocument(vocbase().name(), collectionName, newValue, result.errorNumber()); + return result; } /// @brief replace one or multiple documents in a collection, coordinator @@ -2261,18 +2291,25 @@ OperationResult transaction::Methods::remove(std::string const& collectionName, if (!value.isObject() && !value.isArray() && !value.isString()) { // must provide a document object or an array of documents + events::DeleteDocument(vocbase().name(), collectionName, value, + TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } if (value.isArray() && value.length() == 0) { + events::DeleteDocument(vocbase().name(), collectionName, value, TRI_ERROR_NO_ERROR); return emptyResult(options); } + OperationResult result; if (_state->isCoordinator()) { - return removeCoordinator(collectionName, value, options); + result = removeCoordinator(collectionName, value, options); + } else { + OperationOptions optionsCopy = options; + result = removeLocal(collectionName, value, optionsCopy); } - OperationOptions optionsCopy = options; - return removeLocal(collectionName, value, optionsCopy); + events::DeleteDocument(vocbase().name(), collectionName, value, result.errorNumber()); + return result; } /// @brief remove one or multiple documents in a collection, coordinator @@ -2565,7 +2602,7 @@ OperationResult transaction::Methods::truncate(std::string const& collectionName result = truncateLocal(collectionName, optionsCopy); } - events::TruncateCollection(collectionName, result.errorNumber()); + events::TruncateCollection(vocbase().name(), collectionName, result.errorNumber()); return result; } diff --git a/arangod/Utils/Events.cpp b/arangod/Utils/Events.cpp index cdb7d8a2b9..0b12ee3d3a 100644 --- a/arangod/Utils/Events.cpp +++ b/arangod/Utils/Events.cpp @@ -24,20 +24,39 @@ namespace arangodb { namespace events { -void UnknownAuthenticationMethod(GeneralRequest const*) {} -void CredentialsMissing(GeneralRequest const*) {} -void CredentialsBad(GeneralRequest const*, rest::AuthenticationMethod) {} -void PasswordChangeRequired(GeneralRequest const*) {} -void Authenticated(GeneralRequest const*, rest::AuthenticationMethod) {} -void NotAuthorized(GeneralRequest const*) {} -void CreateCollection(std::string const& name, int result) {} -void DropCollection(std::string const& name, int result) {} -void TruncateCollection(std::string const& name, int result) {} +void UnknownAuthenticationMethod(GeneralRequest const&) {} +void CredentialsMissing(GeneralRequest const&) {} +void LoggedIn(GeneralRequest const&, std::string const& username) {} +void CredentialsBad(GeneralRequest const&, std::string const& username) {} +void CredentialsBad(GeneralRequest const&, rest::AuthenticationMethod) {} +void PasswordChangeRequired(GeneralRequest const&) {} +void Authenticated(GeneralRequest const&, rest::AuthenticationMethod) {} +void NotAuthorized(GeneralRequest const&) {} +void CreateCollection(std::string const& db, std::string const& name, int result) {} +void DropCollection(std::string const& db, std::string const& name, int result) {} +void TruncateCollection(std::string const& db, std::string const& name, int result) {} void CreateDatabase(std::string const& name, int result) {} void DropDatabase(std::string const& name, int result) {} -void CreateIndex(std::string const& col, VPackSlice const&) {} -void DropIndex(std::string const& col, std::string const& idx, int result) {} -void CreateView(std::string const& name, int result) {} -void DropView(std::string const& name, int result) {} +void CreateIndex(std::string const& db, std::string const& col, + VPackSlice const&, int result) {} +void DropIndex(std::string const& db, std::string const& col, + std::string const& idx, int result) {} +void CreateView(std::string const& db, std::string const& name, int result) {} +void DropView(std::string const& db, std::string const& name, int result) {} +void CreateDocument(std::string const& db, std::string const& collection, + VPackSlice const& document, int) {} +void DeleteDocument(std::string const& db, std::string const& collection, + VPackSlice const& document, int) {} +void ReadDocument(std::string const& db, std::string const& collection, + VPackSlice const& document, int) {} +void ReplaceDocument(std::string const& db, std::string const& collection, + VPackSlice const& document, int) {} +void ModifyDocument(std::string const& db, std::string const& collection, + VPackSlice const& document, int) {} +void IllegalDocumentOperation(GeneralRequest const&, int result) {} +void QueryDocument(std::string const& db, std::string const&, std::string const&, int code) {} +void QueryDocument(std::string const& db, VPackSlice const&, int code) {} +void QueryDocument(GeneralRequest const&, GeneralResponse const*, VPackSlice const&) {} + } // namespace events } // namespace arangodb diff --git a/arangod/Utils/Events.h b/arangod/Utils/Events.h index a19cceed27..6610ca5dcc 100644 --- a/arangod/Utils/Events.h +++ b/arangod/Utils/Events.h @@ -29,26 +29,46 @@ #include #include "Rest/CommonDefines.h" +#include "VocBase/LogicalCollection.h" namespace arangodb { class GeneralRequest; +class GeneralResponse; +struct OperationResult; namespace events { -void UnknownAuthenticationMethod(GeneralRequest const*); -void CredentialsMissing(GeneralRequest const*); -void CredentialsBad(GeneralRequest const*, rest::AuthenticationMethod); -void PasswordChangeRequired(GeneralRequest const*); -void Authenticated(GeneralRequest const*, rest::AuthenticationMethod); -void NotAuthorized(GeneralRequest const*); -void CreateCollection(std::string const& name, int result); -void DropCollection(std::string const& name, int result); -void TruncateCollection(std::string const& name, int result); +void UnknownAuthenticationMethod(GeneralRequest const&); +void CredentialsMissing(GeneralRequest const&); +void LoggedIn(GeneralRequest const&, std::string const& username); +void CredentialsBad(GeneralRequest const&, std::string const& username); +void CredentialsBad(GeneralRequest const&, rest::AuthenticationMethod); +void PasswordChangeRequired(GeneralRequest const&); +void Authenticated(GeneralRequest const&, rest::AuthenticationMethod); +void NotAuthorized(GeneralRequest const&); +void CreateCollection(std::string const& db, std::string const& name, int result); +void DropCollection(std::string const& db, std::string const& name, int result); +void TruncateCollection(std::string const& db, std::string const& name, int result); void CreateDatabase(std::string const& name, int result); void DropDatabase(std::string const& name, int result); -void CreateIndex(std::string const& col, VPackSlice const&); -void DropIndex(std::string const& col, std::string const& idx, int result); -void CreateView(std::string const& name, int result); -void DropView(std::string const& name, int result); +void CreateIndex(std::string const& db, std::string const& col, VPackSlice const&, int result); +void DropIndex(std::string const& db, std::string const& col, + std::string const& idx, int result); +void CreateView(std::string const& db, std::string const& name, int result); +void DropView(std::string const& db, std::string const& name, int result); +void CreateDocument(std::string const& db, std::string const& collection, + VPackSlice const& document, int); +void DeleteDocument(std::string const& db, std::string const& collection, + VPackSlice const& document, int); +void ReadDocument(std::string const& db, std::string const& collection, + VPackSlice const& document, int); +void ReplaceDocument(std::string const& db, std::string const& collection, + VPackSlice const& document, int); +void ModifyDocument(std::string const& db, std::string const& collection, + VPackSlice const& document, int); +void IllegalDocumentOperation(GeneralRequest const&, int result); +void QueryDocument(std::string const& db, std::string const&, std::string const&, int code); +void QueryDocument(std::string const& db, VPackSlice const&, int code); +void QueryDocument(GeneralRequest const&, GeneralResponse const*, VPackSlice const&); } // namespace events } // namespace arangodb diff --git a/arangod/V8Server/v8-collection.cpp b/arangod/V8Server/v8-collection.cpp index 3789a50051..a9cab658bb 100644 --- a/arangod/V8Server/v8-collection.cpp +++ b/arangod/V8Server/v8-collection.cpp @@ -51,6 +51,7 @@ #include "Transaction/Hints.h" #include "Transaction/V8Context.h" #include "Utils/CollectionNameResolver.h" +#include "Utils/Events.h" #include "Utils/ExecContext.h" #include "Utils/OperationOptions.h" #include "Utils/OperationResult.h" @@ -873,14 +874,15 @@ static void JS_DropVocbaseCol(v8::FunctionCallbackInfo const& args) { v8::Local context = isolate->GetCurrentContext(); v8::HandleScope scope(isolate); auto& vocbase = GetContextVocBase(isolate); - if (vocbase.isDangling()) { + events::DropCollection(vocbase.name(), "", TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } auto* collection = UnwrapCollection(isolate, args.Holder()); if (!collection) { + events::DropCollection(vocbase.name(), "", TRI_ERROR_INTERNAL); TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } @@ -907,10 +909,17 @@ static void JS_DropVocbaseCol(v8::FunctionCallbackInfo const& args) { } } - auto res = methods::Collections::drop(*collection, allowDropSystem, timeout); - - if (res.fail()) { - TRI_V8_THROW_EXCEPTION(res); + try { + auto res = methods::Collections::drop(*collection, allowDropSystem, timeout); + if (res.fail()) { + TRI_V8_THROW_EXCEPTION(res); + } + } catch (basics::Exception const& ex) { + events::DropCollection(vocbase.name(), collection->name(), ex.code()); + throw; + } catch (...) { + events::DropCollection(vocbase.name(), collection->name(), TRI_ERROR_INTERNAL); + throw; } TRI_V8_RETURN_UNDEFINED(); diff --git a/arangod/V8Server/v8-views.cpp b/arangod/V8Server/v8-views.cpp index a56b3201b3..d2cadc7569 100644 --- a/arangod/V8Server/v8-views.cpp +++ b/arangod/V8Server/v8-views.cpp @@ -30,6 +30,7 @@ #include "RestServer/DatabaseFeature.h" #include "Transaction/V8Context.h" #include "Utils/CollectionNameResolver.h" +#include "Utils/Events.h" #include "Utils/ExecContext.h" #include "V8/v8-conv.h" #include "V8/v8-globals.h" @@ -130,11 +131,13 @@ static void JS_CreateViewVocbase(v8::FunctionCallbackInfo const& args auto& vocbase = GetContextVocBase(isolate); if (vocbase.isDangling()) { + events::CreateView(vocbase.name(), "", TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } // we require exactly 3 arguments if (args.Length() != 3) { + events::CreateView(vocbase.name(), "", TRI_ERROR_FORBIDDEN); TRI_V8_THROW_EXCEPTION_USAGE("_createView(, , )"); } @@ -147,6 +150,7 @@ static void JS_CreateViewVocbase(v8::FunctionCallbackInfo const& args std::string const type = TRI_ObjectToString(isolate, args[1]); if (!args[2]->IsObject()) { + events::CreateView(vocbase.name(), name, TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_TYPE_ERROR(" must be an object"); } @@ -156,6 +160,7 @@ static void JS_CreateViewVocbase(v8::FunctionCallbackInfo const& args int res = TRI_V8ToVPack(isolate, properties, obj, false); if (res != TRI_ERROR_NO_ERROR) { + events::CreateView(vocbase.name(), name, res); TRI_V8_THROW_EXCEPTION(res); } @@ -164,6 +169,7 @@ static void JS_CreateViewVocbase(v8::FunctionCallbackInfo const& args // ........................................................................... if (!canUse(auth::Level::RW, vocbase)) { + events::CreateView(vocbase.name(), name, TRI_ERROR_FORBIDDEN); TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN, "insufficient rights to create view"); } @@ -185,6 +191,7 @@ static void JS_CreateViewVocbase(v8::FunctionCallbackInfo const& args auto res = LogicalView::create(view, vocbase, builder.slice()); if (!res.ok()) { + // events::CreateView(vocbase.name(), name, res.errorNumber()); TRI_V8_THROW_EXCEPTION_MESSAGE(res.errorNumber(), res.errorMessage()); } @@ -201,10 +208,13 @@ static void JS_CreateViewVocbase(v8::FunctionCallbackInfo const& args TRI_V8_RETURN(result); } catch (basics::Exception const& ex) { + events::CreateView(vocbase.name(), name, ex.code()); TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), ex.what()); } catch (std::exception const& ex) { + events::CreateView(vocbase.name(), name, TRI_ERROR_INTERNAL); TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, ex.what()); } catch (...) { + events::CreateView(vocbase.name(), name, TRI_ERROR_INTERNAL); TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "cannot create view"); } TRI_V8_TRY_CATCH_END @@ -217,11 +227,13 @@ static void JS_DropViewVocbase(v8::FunctionCallbackInfo const& args) auto& vocbase = GetContextVocBase(isolate); if (vocbase.isDangling()) { + events::DropView(vocbase.name(), "", TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } // we require exactly 1 string argument and an optional boolean argument if (args.Length() < 1 || args.Length() > 2) { + events::DropView(vocbase.name(), "", TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_EXCEPTION_USAGE("_dropView( [, allowDropSystem])"); } @@ -257,12 +269,14 @@ static void JS_DropViewVocbase(v8::FunctionCallbackInfo const& args) if (view) { if (!view->canUse(auth::Level::RW)) { // check auth after ensuring that the view exists + events::DropView(vocbase.name(), view->name(), TRI_ERROR_FORBIDDEN); TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN, "insufficient rights to drop view"); } // prevent dropping of system views if (!allowDropSystem && view->system()) { + events::DropView(vocbase.name(), view->name(), TRI_ERROR_FORBIDDEN); TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN, "insufficient rights to drop system view"); } @@ -272,6 +286,8 @@ static void JS_DropViewVocbase(v8::FunctionCallbackInfo const& args) if (!res.ok()) { TRI_V8_THROW_EXCEPTION(res); } + } else { + events::DropView(vocbase.name(), name, TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); } TRI_V8_RETURN_UNDEFINED(); @@ -283,10 +299,11 @@ static void JS_DropViewVocbaseObj(v8::FunctionCallbackInfo const& arg TRI_V8_TRY_CATCH_BEGIN(isolate); v8::Local context = isolate->GetCurrentContext(); v8::HandleScope scope(isolate); - + auto& vocbase = GetContextVocBase(isolate); auto* view = UnwrapView(isolate, args.Holder()); if (!view) { + events::DropView(vocbase.name(), "", TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract view"); } @@ -316,12 +333,14 @@ static void JS_DropViewVocbaseObj(v8::FunctionCallbackInfo const& arg // ........................................................................... if (!view->canUse(auth::Level::RW)) { // check auth after ensuring that the view exists + events::DropView(vocbase.name(), view->name(), TRI_ERROR_FORBIDDEN); TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN, "insufficient rights to drop view"); } // prevent dropping of system views if (!allowDropSystem && view->system()) { + events::DropView(vocbase.name(), view->name(), TRI_ERROR_FORBIDDEN); TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN, "insufficient rights to drop system view"); } diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 5c0ece8c75..193f90c480 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -62,6 +62,7 @@ #include "StorageEngine/EngineSelectorFeature.h" #include "StorageEngine/StorageEngine.h" #include "Transaction/V8Context.h" +#include "Utils/Events.h" #include "Utils/ExecContext.h" #include "V8/JSLoader.h" #include "V8/V8LineEditor.h" @@ -672,10 +673,12 @@ static void JS_ExecuteAqlJson(v8::FunctionCallbackInfo const& args) { auto& vocbase = GetContextVocBase(isolate); if (args.Length() < 1 || args.Length() > 2) { + events::QueryDocument(vocbase.name(), VPackSlice(), TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_EXCEPTION_USAGE("AQL_EXECUTEJSON(, )"); } if (!args[0]->IsObject()) { + events::QueryDocument(vocbase.name(), VPackSlice(), TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_TYPE_ERROR("expecting object for "); } @@ -683,6 +686,7 @@ static void JS_ExecuteAqlJson(v8::FunctionCallbackInfo const& args) { int res = TRI_V8ToVPack(isolate, *queryBuilder, args[0], false); if (res != TRI_ERROR_NO_ERROR) { + events::QueryDocument(vocbase.name(), VPackSlice(), res); TRI_V8_THROW_EXCEPTION(res); } @@ -691,11 +695,13 @@ static void JS_ExecuteAqlJson(v8::FunctionCallbackInfo const& args) { if (args.Length() > 1) { // we have options! yikes! if (!args[1]->IsUndefined() && !args[1]->IsObject()) { + events::QueryDocument(vocbase.name(), queryBuilder->slice(), TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_TYPE_ERROR("expecting object for "); } res = TRI_V8ToVPack(isolate, *options, args[1], false); if (res != TRI_ERROR_NO_ERROR) { + events::QueryDocument(vocbase.name(), queryBuilder->slice(), res); TRI_V8_THROW_EXCEPTION(res); } } @@ -706,6 +712,7 @@ static void JS_ExecuteAqlJson(v8::FunctionCallbackInfo const& args) { query.executeSync(static_cast(v8g->_queryRegistry)); if (queryResult.result.fail()) { + events::QueryDocument(vocbase.name(), queryBuilder->slice(), queryResult.result.errorNumber()); TRI_V8_THROW_EXCEPTION_FULL(queryResult.result.errorNumber(), queryResult.result.errorMessage()); } @@ -738,6 +745,8 @@ static void JS_ExecuteAqlJson(v8::FunctionCallbackInfo const& args) { result->Set(TRI_V8_ASCII_STRING(isolate, "cached"), v8::Boolean::New(isolate, queryResult.cached)); + events::QueryDocument(vocbase.name(), queryBuilder->slice(), TRI_ERROR_NO_ERROR); + TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } @@ -752,12 +761,14 @@ static void JS_ExecuteAql(v8::FunctionCallbackInfo const& args) { auto& vocbase = GetContextVocBase(isolate); if (args.Length() < 1 || args.Length() > 3) { + events::QueryDocument(vocbase.name(), "", "", TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_EXCEPTION_USAGE( "AQL_EXECUTE(, , )"); } // get the query string if (!args[0]->IsString()) { + events::QueryDocument(vocbase.name(), "", "", TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_TYPE_ERROR("expecting string for "); } @@ -768,6 +779,7 @@ static void JS_ExecuteAql(v8::FunctionCallbackInfo const& args) { if (args.Length() > 1) { if (!args[1]->IsUndefined() && !args[1]->IsNull() && !args[1]->IsObject()) { + events::QueryDocument(vocbase.name(), queryString, "", TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_TYPE_ERROR("expecting object for "); } if (args[1]->IsObject()) { @@ -775,6 +787,7 @@ static void JS_ExecuteAql(v8::FunctionCallbackInfo const& args) { int res = TRI_V8ToVPack(isolate, *(bindVars.get()), args[1], false); if (res != TRI_ERROR_NO_ERROR) { + events::QueryDocument(vocbase.name(), queryString, "", res); TRI_V8_THROW_EXCEPTION(res); } } @@ -785,11 +798,16 @@ static void JS_ExecuteAql(v8::FunctionCallbackInfo const& args) { if (args.Length() > 2) { // we have options! yikes! if (!args[2]->IsObject()) { + events::QueryDocument(vocbase.name(), queryString, + (bindVars ? bindVars->slice().toJson() : ""), + TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_TYPE_ERROR("expecting object for "); } int res = TRI_V8ToVPack(isolate, *options, args[2], false); if (res != TRI_ERROR_NO_ERROR) { + events::QueryDocument(vocbase.name(), queryString, + (bindVars ? bindVars->slice().toJson() : ""), res); TRI_V8_THROW_EXCEPTION(res); } } @@ -817,9 +835,15 @@ static void JS_ExecuteAql(v8::FunctionCallbackInfo const& args) { if (queryResult.result.is(TRI_ERROR_REQUEST_CANCELED)) { TRI_GET_GLOBALS(); v8g->_canceled = true; + events::QueryDocument(vocbase.name(), queryString, + (bindVars ? bindVars->slice().toJson() : ""), + TRI_ERROR_REQUEST_CANCELED); TRI_V8_THROW_EXCEPTION(TRI_ERROR_REQUEST_CANCELED); } + events::QueryDocument(vocbase.name(), queryString, + (bindVars ? bindVars->slice().toJson() : ""), + queryResult.result.errorNumber()); TRI_V8_THROW_EXCEPTION_FULL(queryResult.result.errorNumber(), queryResult.result.errorMessage()); } @@ -854,6 +878,9 @@ static void JS_ExecuteAql(v8::FunctionCallbackInfo const& args) { result->Set(TRI_V8_ASCII_STRING(isolate, "cached"), v8::Boolean::New(isolate, queryResult.cached)); + events::QueryDocument(vocbase.name(), queryString, + (bindVars ? bindVars->slice().toJson() : ""), TRI_ERROR_NO_ERROR); + TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } @@ -1655,6 +1682,7 @@ static void JS_CreateDatabase(v8::FunctionCallbackInfo const& args) { v8::HandleScope scope(isolate); if (args.Length() < 1 || args.Length() > 3) { + events::CreateDatabase("", TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_EXCEPTION_USAGE( "db._createDatabase(, , )"); } @@ -1664,6 +1692,7 @@ static void JS_CreateDatabase(v8::FunctionCallbackInfo const& args) { TRI_ASSERT(!vocbase.isDangling()); if (!vocbase.isSystem()) { + events::CreateDatabase("", TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); } @@ -1683,6 +1712,7 @@ static void JS_CreateDatabase(v8::FunctionCallbackInfo const& args) { v8::Handle user = ar->Get(i); if (!user->IsObject()) { + events::CreateDatabase("", TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER, "user is not an object"); } @@ -1711,18 +1741,21 @@ static void JS_DropDatabase(v8::FunctionCallbackInfo const& args) { v8::HandleScope scope(isolate); if (args.Length() != 1) { + events::DropDatabase("", TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_EXCEPTION_USAGE("db._dropDatabase()"); } auto& vocbase = GetContextVocBase(isolate); if (!vocbase.isSystem()) { + events::DropDatabase("", TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); } ExecContext const* exec = ExecContext::CURRENT; if (exec != nullptr && exec->systemAuthLevel() != auth::Level::RW) { + events::DropDatabase("", TRI_ERROR_FORBIDDEN); TRI_V8_THROW_EXCEPTION(TRI_ERROR_FORBIDDEN); } diff --git a/arangod/V8Server/v8-vocindex.cpp b/arangod/V8Server/v8-vocindex.cpp index 4649f9ada1..be84cbfaf6 100644 --- a/arangod/V8Server/v8-vocindex.cpp +++ b/arangod/V8Server/v8-vocindex.cpp @@ -127,16 +127,19 @@ static void JS_LookupIndexVocbaseCol(v8::FunctionCallbackInfo const& static void JS_DropIndexVocbaseCol(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); + auto& vocbase = GetContextVocBase(isolate); PREVENT_EMBEDDED_TRANSACTION(); auto* collection = UnwrapCollection(isolate, args.Holder()); if (!collection) { + events::DropIndex(vocbase.name(), "", "", TRI_ERROR_INTERNAL); TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } if (args.Length() != 1) { + events::DropIndex(vocbase.name(), "", "", TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_EXCEPTION_USAGE("dropIndex()"); } @@ -202,14 +205,17 @@ static void CreateVocBase(v8::FunctionCallbackInfo const& args, auto& vocbase = GetContextVocBase(isolate); if (vocbase.isDangling()) { + events::CreateCollection(vocbase.name(), "", TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } else if (args.Length() < 1 || args.Length() > 4) { + events::CreateCollection(vocbase.name(), "", TRI_ERROR_BAD_PARAMETER); TRI_V8_THROW_EXCEPTION_USAGE( "_create(, , , )"); } if (ExecContext::CURRENT != nullptr && !ExecContext::CURRENT->canUseDatabase(vocbase.name(), auth::Level::RW)) { + events::CreateCollection(vocbase.name(), "", TRI_ERROR_FORBIDDEN); TRI_V8_THROW_EXCEPTION(TRI_ERROR_FORBIDDEN); } diff --git a/arangod/VocBase/LogicalView.cpp b/arangod/VocBase/LogicalView.cpp index 8b7fd1226f..45beceb84a 100644 --- a/arangod/VocBase/LogicalView.cpp +++ b/arangod/VocBase/LogicalView.cpp @@ -31,6 +31,7 @@ #include "RestServer/ViewTypesFeature.h" #include "StorageEngine/EngineSelectorFeature.h" #include "StorageEngine/StorageEngine.h" +#include "Utils/Events.h" #include "Utils/ExecContext.h" #include "VocBase/ticks.h" #include "VocBase/vocbase.h" @@ -111,6 +112,12 @@ bool LogicalView::canUse(arangodb::auth::Level const& level) { application_features::ApplicationServer::lookupFeature(); if (!viewTypes) { + std::string name; + if (definition.isObject()) { + name = basics::VelocyPackHelper::getStringValue(definition, StaticStrings::DataSourceName, + ""); + } + events::CreateView(vocbase.name(), name, TRI_ERROR_INTERNAL); return Result( TRI_ERROR_INTERNAL, "Failure to get 'ViewTypes' feature while creating LogicalView"); @@ -528,4 +535,4 @@ Result LogicalView::rename(std::string&& newName) { // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- \ No newline at end of file +// ----------------------------------------------------------------------------- diff --git a/arangod/VocBase/Methods/Collections.cpp b/arangod/VocBase/Methods/Collections.cpp index 2b3768eb7a..7d6f5563f7 100644 --- a/arangod/VocBase/Methods/Collections.cpp +++ b/arangod/VocBase/Methods/Collections.cpp @@ -42,6 +42,7 @@ #include "Sharding/ShardingFeature.h" #include "StorageEngine/PhysicalCollection.h" #include "Transaction/V8Context.h" +#include "Utils/Events.h" #include "Utils/ExecContext.h" #include "Utils/OperationCursor.h" #include "Utils/SingleCollectionTransaction.h" @@ -211,14 +212,17 @@ void Collections::enumerate(TRI_vocbase_t* vocbase, FuncCallback func // invoke on collection creation ) { if (name.empty()) { + events::CreateCollection(vocbase.name(), name, TRI_ERROR_ARANGO_ILLEGAL_NAME); return TRI_ERROR_ARANGO_ILLEGAL_NAME; } else if (collectionType != TRI_col_type_e::TRI_COL_TYPE_DOCUMENT && collectionType != TRI_col_type_e::TRI_COL_TYPE_EDGE) { + events::CreateCollection(vocbase.name(), name, TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID); return TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID; } ExecContext const* exec = ExecContext::CURRENT; if (exec && !exec->canUseDatabase(vocbase.name(), auth::Level::RW)) { + events::CreateCollection(vocbase.name(), name, TRI_ERROR_FORBIDDEN); return arangodb::Result( // result TRI_ERROR_FORBIDDEN, // code "cannot create collection in " + vocbase.name() // message @@ -265,6 +269,7 @@ void Collections::enumerate(TRI_vocbase_t* vocbase, enforceReplicationFactor); if (!col) { + events::CreateCollection(vocbase.name(), name, TRI_ERROR_INTERNAL); return Result(TRI_ERROR_INTERNAL, "createCollectionOnCoordinator"); } @@ -281,6 +286,7 @@ void Collections::enumerate(TRI_vocbase_t* vocbase, while (true) { Result r = um->updateUser(ExecContext::CURRENT->user(), [&](auth::User& entry) { entry.grantCollection(vocbase.name(), name, auth::Level::RW); + events::CreateCollection(vocbase.name(), name, TRI_ERROR_NO_ERROR); return TRI_ERROR_NO_ERROR; }); @@ -296,6 +302,7 @@ void Collections::enumerate(TRI_vocbase_t* vocbase, << "Updating user failed with error: " << r.errorMessage() << ". giving up!"; + events::CreateCollection(vocbase.name(), name, r.errorNumber()); return r; } @@ -324,6 +331,7 @@ void Collections::enumerate(TRI_vocbase_t* vocbase, while (true) { Result r = um->updateUser(ExecContext::CURRENT->user(), [&](auth::User& entry) { entry.grantCollection(vocbase.name(), name, auth::Level::RW); + events::CreateCollection(vocbase.name(), name, TRI_ERROR_NO_ERROR); return TRI_ERROR_NO_ERROR; }); @@ -339,6 +347,7 @@ void Collections::enumerate(TRI_vocbase_t* vocbase, << "Updating user failed with error: " << r.errorMessage() << ". giving up!"; + events::CreateCollection(vocbase.name(), name, r.errorNumber()); return r; } @@ -352,13 +361,18 @@ void Collections::enumerate(TRI_vocbase_t* vocbase, func(col); } } catch (basics::Exception const& ex) { + events::CreateCollection(vocbase.name(), name, ex.code()); return Result(ex.code(), ex.what()); } catch (std::exception const& ex) { + events::CreateCollection(vocbase.name(), name, TRI_ERROR_INTERNAL); return Result(TRI_ERROR_INTERNAL, ex.what()); } catch (...) { + events::CreateCollection(vocbase.name(), name, TRI_ERROR_INTERNAL); return Result(TRI_ERROR_INTERNAL, "cannot create collection"); } + events::CreateCollection(vocbase.name(), name, TRI_ERROR_NO_ERROR); + return TRI_ERROR_NO_ERROR; } @@ -612,6 +626,7 @@ static Result DropVocbaseColCoordinator(arangodb::LogicalCollection* collection, && exec->canUseCollection(coll.name(), auth::Level::RW) // collection modifiable ) ) { + events::DropCollection(coll.vocbase().name(), coll.name(), TRI_ERROR_FORBIDDEN); return arangodb::Result( // result TRI_ERROR_FORBIDDEN, // code "Insufficient rights to drop collection " + coll.name() // message @@ -660,6 +675,8 @@ static Result DropVocbaseColCoordinator(arangodb::LogicalCollection* collection, } } + events::DropCollection(coll.vocbase().name(), coll.name(), res.errorNumber()); + return res; } diff --git a/arangod/VocBase/Methods/Databases.cpp b/arangod/VocBase/Methods/Databases.cpp index 1cbfe8a60c..94df56b649 100644 --- a/arangod/VocBase/Methods/Databases.cpp +++ b/arangod/VocBase/Methods/Databases.cpp @@ -32,6 +32,7 @@ #include "Rest/HttpRequest.h" #include "RestServer/DatabaseFeature.h" #include "RestServer/SystemDatabaseFeature.h" +#include "Utils/Events.h" #include "Utils/ExecContext.h" #include "V8/v8-utils.h" #include "V8/v8-vpack.h" @@ -126,6 +127,7 @@ arangodb::Result Databases::create(std::string const& dbName, VPackSlice const& ExecContext const* exec = ExecContext::CURRENT; if (exec != nullptr) { if (!exec->isAdminUser()) { + events::CreateDatabase(dbName, TRI_ERROR_FORBIDDEN); return TRI_ERROR_FORBIDDEN; } } @@ -134,12 +136,14 @@ arangodb::Result Databases::create(std::string const& dbName, VPackSlice const& if (options.isNone() || options.isNull()) { options = VPackSlice::emptyObjectSlice(); } else if (!options.isObject()) { + events::CreateDatabase(dbName, TRI_ERROR_HTTP_BAD_PARAMETER); return Result(TRI_ERROR_HTTP_BAD_PARAMETER, "invalid options slice"); } VPackSlice users = inUsers; if (users.isNone() || users.isNull()) { users = VPackSlice::emptyArraySlice(); } else if (!users.isArray()) { + events::CreateDatabase(dbName, TRI_ERROR_HTTP_BAD_PARAMETER); return Result(TRI_ERROR_HTTP_BAD_PARAMETER, "invalid users slice"); } @@ -148,6 +152,7 @@ arangodb::Result Databases::create(std::string const& dbName, VPackSlice const& for (VPackSlice const& user : VPackArrayIterator(users)) { sanitizedUsers.openObject(); if (!user.isObject()) { + events::CreateDatabase(dbName, TRI_ERROR_HTTP_BAD_PARAMETER); return Result(TRI_ERROR_HTTP_BAD_PARAMETER); } @@ -158,6 +163,7 @@ arangodb::Result Databases::create(std::string const& dbName, VPackSlice const& name = user.get("user"); } if (!name.isString()) { // empty names are silently ignored later + events::CreateDatabase(dbName, TRI_ERROR_HTTP_BAD_PARAMETER); return Result(TRI_ERROR_HTTP_BAD_PARAMETER); } sanitizedUsers.add("username", name); @@ -165,6 +171,7 @@ arangodb::Result Databases::create(std::string const& dbName, VPackSlice const& if (user.hasKey("passwd")) { VPackSlice passwd = user.get("passwd"); if (!passwd.isString()) { + events::CreateDatabase(dbName, TRI_ERROR_HTTP_BAD_PARAMETER); return Result(TRI_ERROR_HTTP_BAD_PARAMETER); } sanitizedUsers.add("passwd", passwd); @@ -189,12 +196,14 @@ arangodb::Result Databases::create(std::string const& dbName, VPackSlice const& DatabaseFeature* databaseFeature = DatabaseFeature::DATABASE; if (databaseFeature == nullptr) { + events::CreateDatabase(dbName, TRI_ERROR_INTERNAL); return Result(TRI_ERROR_INTERNAL); } UpgradeResult upgradeRes; if (ServerState::instance()->isCoordinator()) { if (!TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef(dbName))) { + events::CreateDatabase(dbName, TRI_ERROR_ARANGO_DATABASE_NAME_INVALID); return Result(TRI_ERROR_ARANGO_DATABASE_NAME_INVALID); } @@ -215,6 +224,7 @@ arangodb::Result Databases::create(std::string const& dbName, VPackSlice const& auto res = ci->createDatabaseCoordinator(dbName, builder.slice(), 120.0); if (!res.ok()) { + events::CreateDatabase(dbName, res.errorNumber()); return res; } @@ -313,6 +323,7 @@ int dropDBCoordinator(std::string const& dbName) { TRI_vocbase_t* vocbase = databaseFeature->useDatabase(dbName); if (vocbase == nullptr) { + events::DropDatabase(dbName, TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); return TRI_ERROR_ARANGO_DATABASE_NOT_FOUND; } @@ -324,6 +335,7 @@ int dropDBCoordinator(std::string const& dbName) { auto res = ci->dropDatabaseCoordinator(dbName, 120.0); if (!res.ok()) { + events::DropDatabase(dbName, res.errorNumber()); return res.errorNumber(); } @@ -351,6 +363,7 @@ arangodb::Result Databases::drop(TRI_vocbase_t* systemVocbase, std::string const ExecContext const* exec = ExecContext::CURRENT; if (exec != nullptr) { if (exec->systemAuthLevel() != auth::Level::RW) { + events::DropDatabase(dbName, TRI_ERROR_FORBIDDEN); return TRI_ERROR_FORBIDDEN; } } @@ -360,6 +373,7 @@ arangodb::Result Databases::drop(TRI_vocbase_t* systemVocbase, std::string const if (dealer != nullptr && dealer->isEnabled()) { V8Context* v8ctx = V8DealerFeature::DEALER->enterContext(systemVocbase, true); if (v8ctx == nullptr) { + events::DropDatabase(dbName, TRI_ERROR_INTERNAL); return Result(TRI_ERROR_INTERNAL, "Could not get v8 context"); } TRI_DEFER(V8DealerFeature::DEALER->exitContext(v8ctx)); @@ -376,6 +390,7 @@ arangodb::Result Databases::drop(TRI_vocbase_t* systemVocbase, std::string const res = DatabaseFeature::DATABASE->dropDatabase(dbName, false, true); if (res != TRI_ERROR_NO_ERROR) { + events::DropDatabase(dbName, res); return Result(res); } diff --git a/arangod/VocBase/Methods/Indexes.cpp b/arangod/VocBase/Methods/Indexes.cpp index cb0ba14216..d25ed871b2 100644 --- a/arangod/VocBase/Methods/Indexes.cpp +++ b/arangod/VocBase/Methods/Indexes.cpp @@ -334,6 +334,8 @@ Result Indexes::ensureIndex(LogicalCollection* collection, VPackSlice const& inp bool canRead = exec->canUseCollection(collection->name(), auth::Level::RO); if ((create && (lvl != auth::Level::RW || !canModify)) || (lvl == auth::Level::NONE || !canRead)) { + events::CreateIndex(collection->vocbase().name(), collection->name(), + input, TRI_ERROR_FORBIDDEN); return Result(TRI_ERROR_FORBIDDEN); } } @@ -346,6 +348,8 @@ Result Indexes::ensureIndex(LogicalCollection* collection, VPackSlice const& inp ); if (res.fail()) { + events::CreateIndex(collection->vocbase().name(), collection->name(), input, + res.errorNumber()); return res; } @@ -390,6 +394,8 @@ Result Indexes::ensureIndex(LogicalCollection* collection, VPackSlice const& inp VPackSlice f = flds.at(i); if (!f.isString()) { // index attributes must be strings + events::CreateIndex(collection->vocbase().name(), collection->name(), + indexDef, TRI_ERROR_INTERNAL); return Result(TRI_ERROR_INTERNAL, "index field names should be strings"); } @@ -399,6 +405,8 @@ Result Indexes::ensureIndex(LogicalCollection* collection, VPackSlice const& inp // all shard-keys must be covered by the index for (auto& it : shardKeys) { if (indexKeys.find(it) == indexKeys.end()) { + events::CreateIndex(collection->vocbase().name(), collection->name(), + indexDef, TRI_ERROR_CLUSTER_UNSUPPORTED); return Result(TRI_ERROR_CLUSTER_UNSUPPORTED, "shard key '" + it + "' must be present in unique index"); @@ -410,7 +418,8 @@ Result Indexes::ensureIndex(LogicalCollection* collection, VPackSlice const& inp } TRI_ASSERT(!indexDef.isNone()); - events::CreateIndex(collection->name(), indexDef); + events::CreateIndex(collection->vocbase().name(), collection->name(), + indexDef, TRI_ERROR_NO_ERROR); // ensure an index, coordinator case if (ServerState::instance()->isCoordinator()) { @@ -421,10 +430,14 @@ Result Indexes::ensureIndex(LogicalCollection* collection, VPackSlice const& inp Result res = Indexes::ensureIndexCoordinator(collection, indexDef, create, tmp); #endif if (!res.ok()) { + events::CreateIndex(collection->vocbase().name(), collection->name(), + indexDef, res.errorNumber()); return res; } else if (tmp.slice().isNone()) { // did not find a suitable index - return Result(create ? TRI_ERROR_OUT_OF_MEMORY : TRI_ERROR_ARANGO_INDEX_NOT_FOUND); + int code = create ? TRI_ERROR_OUT_OF_MEMORY : TRI_ERROR_ARANGO_INDEX_NOT_FOUND; + events::CreateIndex(collection->vocbase().name(), collection->name(), indexDef, code); + return Result(code); } // flush estimates @@ -438,9 +451,14 @@ Result Indexes::ensureIndex(LogicalCollection* collection, VPackSlice const& inp VPackValue(collection->name() + TRI_INDEX_HANDLE_SEPARATOR_CHR + iid)); b.close(); output = VPackCollection::merge(tmp.slice(), b.slice(), false); + events::CreateIndex(collection->vocbase().name(), collection->name(), + indexDef, res.errorNumber()); return res; } else { - return EnsureIndexLocal(collection, indexDef, create, output); + Result res = EnsureIndexLocal(collection, indexDef, create, output); + events::CreateIndex(collection->vocbase().name(), collection->name(), + indexDef, res.errorNumber()); + return res; } } @@ -542,9 +560,11 @@ Result Indexes::extractHandle(arangodb::LogicalCollection const* collection, } arangodb::Result Indexes::drop(LogicalCollection* collection, VPackSlice const& indexArg) { + TRI_ASSERT(collection); if (ExecContext::CURRENT != nullptr) { if (ExecContext::CURRENT->databaseAuthLevel() != auth::Level::RW || !ExecContext::CURRENT->canUseCollection(collection->name(), auth::Level::RW)) { + events::DropIndex(collection->vocbase().name(), collection->name(), "", TRI_ERROR_FORBIDDEN); return TRI_ERROR_FORBIDDEN; } } @@ -555,6 +575,8 @@ arangodb::Result Indexes::drop(LogicalCollection* collection, VPackSlice const& Result res = Indexes::extractHandle(collection, &resolver, indexArg, iid); if (!res.ok()) { + events::DropIndex(collection->vocbase().name(), collection->name(), "", + res.errorNumber()); return res; } @@ -562,15 +584,15 @@ arangodb::Result Indexes::drop(LogicalCollection* collection, VPackSlice const& collection->flushClusterIndexEstimates(); #ifdef USE_ENTERPRISE - return Indexes::dropCoordinatorEE(collection, iid); + res = Indexes::dropCoordinatorEE(collection, iid); #else - TRI_ASSERT(collection); - auto& databaseName = collection->vocbase().name(); - - return ClusterInfo::instance()->dropIndexCoordinator( // drop index - databaseName, std::to_string(collection->id()), iid, 0.0 // args + res = ClusterInfo::instance()->dropIndexCoordinator( // drop index + collection->vocbase().name(), std::to_string(collection->id()), iid, 0.0 // args ); #endif + events::DropIndex(collection->vocbase().name(), collection->name(), + std::to_string(iid), res.errorNumber()); + return res; } else { READ_LOCKER(readLocker, collection->vocbase()._inventoryLock); @@ -580,24 +602,35 @@ arangodb::Result Indexes::drop(LogicalCollection* collection, VPackSlice const& Result res = trx.begin(); if (!res.ok()) { + events::DropIndex(collection->vocbase().name(), collection->name(), "", + res.errorNumber()); return res; } LogicalCollection* col = trx.documentCollection(); res = Indexes::extractHandle(collection, trx.resolver(), indexArg, iid); if (!res.ok()) { + events::DropIndex(collection->vocbase().name(), collection->name(), + std::to_string(iid), res.errorNumber()); return res; } std::shared_ptr idx = collection->lookupIndex(iid); if (!idx || idx->id() == 0) { + events::DropIndex(collection->vocbase().name(), collection->name(), + std::to_string(iid), TRI_ERROR_ARANGO_INDEX_NOT_FOUND); return Result(TRI_ERROR_ARANGO_INDEX_NOT_FOUND); } if (!idx->canBeDropped()) { + events::DropIndex(collection->vocbase().name(), collection->name(), + std::to_string(iid), TRI_ERROR_FORBIDDEN); return Result(TRI_ERROR_FORBIDDEN); } bool ok = col->dropIndex(idx->id()); - return ok ? Result() : Result(TRI_ERROR_FAILED); + int code = ok ? TRI_ERROR_NO_ERROR : TRI_ERROR_FAILED; + events::DropIndex(collection->vocbase().name(), collection->name(), + std::to_string(iid), code); + return Result(code); } } diff --git a/arangod/VocBase/vocbase.cpp b/arangod/VocBase/vocbase.cpp index bcc74ee0e7..eacef5b77b 100644 --- a/arangod/VocBase/vocbase.cpp +++ b/arangod/VocBase/vocbase.cpp @@ -409,7 +409,7 @@ std::shared_ptr TRI_vocbase_t::createCollectionWork auto it = _dataSourceByName.find(name); if (it != _dataSourceByName.end()) { - events::CreateCollection(name, TRI_ERROR_ARANGO_DUPLICATE_NAME); + events::CreateCollection(this->name(), name, TRI_ERROR_ARANGO_DUPLICATE_NAME); THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DUPLICATE_NAME); } @@ -423,7 +423,7 @@ std::shared_ptr TRI_vocbase_t::createCollectionWork // Let's try to persist it. collection->persistPhysicalCollection(); - events::CreateCollection(name, TRI_ERROR_NO_ERROR); + events::CreateCollection(this->name(), name, TRI_ERROR_NO_ERROR); return collection; } catch (...) { @@ -639,7 +639,7 @@ int TRI_vocbase_t::dropCollectionWorker(arangodb::LogicalCollection* collection, TRI_ASSERT(!locker.isLocked()); if (timeout >= 0.0 && TRI_microtime() > startTime + timeout) { - events::DropCollection(colName, TRI_ERROR_LOCK_TIMEOUT); + events::DropCollection(name(), colName, TRI_ERROR_LOCK_TIMEOUT); return TRI_ERROR_LOCK_TIMEOUT; } @@ -685,11 +685,11 @@ int TRI_vocbase_t::dropCollectionWorker(arangodb::LogicalCollection* collection, engine->changeCollection(*this, *collection, doSync); } catch (arangodb::basics::Exception const& ex) { collection->deleted(false); - events::DropCollection(colName, ex.code()); + events::DropCollection(name(), colName, ex.code()); return ex.code(); } catch (std::exception const&) { collection->deleted(false); - events::DropCollection(colName, TRI_ERROR_INTERNAL); + events::DropCollection(name(), colName, TRI_ERROR_INTERNAL); return TRI_ERROR_INTERNAL; } } @@ -720,6 +720,7 @@ int TRI_vocbase_t::dropCollectionWorker(arangodb::LogicalCollection* collection, false); // always a full-update if (!res.ok()) { + events::DropCollection(name(), colName, res.errorNumber()); return res.errorNumber(); } @@ -735,11 +736,11 @@ int TRI_vocbase_t::dropCollectionWorker(arangodb::LogicalCollection* collection, } default: { // unknown status - events::DropCollection(colName, TRI_ERROR_INTERNAL); + events::DropCollection(name(), colName, TRI_ERROR_INTERNAL); return TRI_ERROR_INTERNAL; } } - events::DropCollection(colName, TRI_ERROR_NO_ERROR); + events::DropCollection(name(), colName, TRI_ERROR_NO_ERROR); return TRI_ERROR_NO_ERROR; } @@ -1078,6 +1079,12 @@ std::shared_ptr TRI_vocbase_t::createCollection( arangodb::velocypack::Slice parameters) { // check that the name does not contain any strange characters if (!IsAllowedName(parameters)) { + std::string name; + if (parameters.isObject()) { + name = VelocyPackHelper::getStringValue(parameters, + StaticStrings::DataSourceName, ""); + } + events::CreateCollection(this->name(), name, TRI_ERROR_ARANGO_ILLEGAL_NAME); THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_ILLEGAL_NAME); } @@ -1190,12 +1197,14 @@ arangodb::Result TRI_vocbase_t::dropCollection(TRI_voc_cid_t cid, auto collection = lookupCollection(cid); if (!collection) { + events::DropCollection(name(), "", TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); return TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND; } StorageEngine* engine = EngineSelectorFeature::ENGINE; if (!engine) { + events::DropCollection(name(), collection->name(), TRI_ERROR_INTERNAL); return arangodb::Result( TRI_ERROR_INTERNAL, std::string( @@ -1205,6 +1214,7 @@ arangodb::Result TRI_vocbase_t::dropCollection(TRI_voc_cid_t cid, if (!allowDropSystem && collection->system() && !engine->inRecovery()) { // prevent dropping of system collections + events::DropCollection(name(), collection->name(), TRI_ERROR_FORBIDDEN); return TRI_set_errno(TRI_ERROR_FORBIDDEN); } @@ -1231,6 +1241,7 @@ arangodb::Result TRI_vocbase_t::dropCollection(TRI_voc_cid_t cid, } if (state == DROP_PERFORM || state == DROP_EXIT) { + events::DropCollection(name(), collection->name(), res); return res; } @@ -1501,6 +1512,12 @@ std::shared_ptr TRI_vocbase_t::createView(arangodb::veloc auto* engine = EngineSelectorFeature::ENGINE; if (!engine) { + std::string n; + if (parameters.isObject()) { + n = VelocyPackHelper::getStringValue(parameters, + StaticStrings::DataSourceName, ""); + } + events::CreateView(name(), n, TRI_ERROR_INTERNAL); THROW_ARANGO_EXCEPTION_MESSAGE( TRI_ERROR_INTERNAL, "failure to get storage engine during creation of view"); @@ -1508,6 +1525,12 @@ std::shared_ptr TRI_vocbase_t::createView(arangodb::veloc // check that the name does not contain any strange characters if (!IsAllowedName(parameters)) { + std::string n; + if (parameters.isObject()) { + n = VelocyPackHelper::getStringValue(parameters, + StaticStrings::DataSourceName, ""); + } + events::CreateView(name(), n, TRI_ERROR_ARANGO_ILLEGAL_NAME); THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_ILLEGAL_NAME); } @@ -1515,6 +1538,12 @@ std::shared_ptr TRI_vocbase_t::createView(arangodb::veloc auto res = LogicalView::instantiate(view, *this, parameters); if (!res.ok() || !view) { + std::string n; + if (parameters.isObject()) { + n = VelocyPackHelper::getStringValue(parameters, + StaticStrings::DataSourceName, ""); + } + events::CreateView(name(), n, TRI_ERROR_INTERNAL); THROW_ARANGO_EXCEPTION_MESSAGE( TRI_ERROR_INTERNAL, std::string("failed to instantiate view from definition: ") + parameters.toString()); @@ -1525,8 +1554,7 @@ std::shared_ptr TRI_vocbase_t::createView(arangodb::veloc auto itr = _dataSourceByName.find(view->name()); if (itr != _dataSourceByName.end()) { - events::CreateView(view->name(), TRI_ERROR_ARANGO_DUPLICATE_NAME); - + events::CreateView(name(), view->name(), TRI_ERROR_ARANGO_DUPLICATE_NAME); THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DUPLICATE_NAME); } @@ -1541,11 +1569,11 @@ std::shared_ptr TRI_vocbase_t::createView(arangodb::veloc } } catch (...) { unregisterView(*view); - + events::CreateView(name(), view->name(), TRI_ERROR_INTERNAL); throw; } - events::CreateView(view->name(), TRI_ERROR_NO_ERROR); + events::CreateView(name(), view->name(), TRI_ERROR_NO_ERROR); if (DatabaseFeature::DATABASE != nullptr && DatabaseFeature::DATABASE->versionTracker() != nullptr) { @@ -1563,12 +1591,14 @@ arangodb::Result TRI_vocbase_t::dropView(TRI_voc_cid_t cid, bool allowDropSystem auto const view = lookupView(cid); if (!view) { + events::DropView(name(), "", TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); return TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND; } StorageEngine* engine = EngineSelectorFeature::ENGINE; if (!engine) { + events::DropView(name(), view->name(), TRI_ERROR_INTERNAL); return arangodb::Result( TRI_ERROR_INTERNAL, std::string("failed to find StorageEngine while dropping view '") + @@ -1576,6 +1606,7 @@ arangodb::Result TRI_vocbase_t::dropView(TRI_voc_cid_t cid, bool allowDropSystem } if (!allowDropSystem && view->system() && !engine->inRecovery()) { + events::DropView(name(), view->name(), TRI_ERROR_FORBIDDEN); return TRI_ERROR_FORBIDDEN; // prevent dropping of system views } @@ -1618,6 +1649,7 @@ arangodb::Result TRI_vocbase_t::dropView(TRI_voc_cid_t cid, bool allowDropSystem auto res = engine->dropView(*this, *view); if (!res.ok()) { + events::DropView(name(), view->name(), res.errorNumber()); return res; } @@ -1632,7 +1664,7 @@ arangodb::Result TRI_vocbase_t::dropView(TRI_voc_cid_t cid, bool allowDropSystem locker.unlock(); writeLocker.unlock(); - events::DropView(view->name(), TRI_ERROR_NO_ERROR); + events::DropView(name(), view->name(), TRI_ERROR_NO_ERROR); if (DatabaseFeature::DATABASE != nullptr && DatabaseFeature::DATABASE->versionTracker() != nullptr) { diff --git a/js/client/modules/@arangodb/process-utils.js b/js/client/modules/@arangodb/process-utils.js index 7e5a644cb8..c356bde4ac 100755 --- a/js/client/modules/@arangodb/process-utils.js +++ b/js/client/modules/@arangodb/process-utils.js @@ -1482,6 +1482,11 @@ function startArango (protocol, options, addArgs, rootDir, role) { args['server.endpoint'] = endpoint; args['database.directory'] = dataDir; args['log.file'] = fs.join(rootDir, 'log'); + if (options.auditLoggingEnabled) { + args['audit.output'] = 'file://' + fs.join(rootDir, 'audit.log'); + args['server.statistics'] = false; + args['foxx.queues'] = false; + } if (protocol === 'ssl') { args['ssl.keyfile'] = fs.join('UnitTests', 'server.pem'); diff --git a/js/client/modules/@arangodb/testsuites/audit.js b/js/client/modules/@arangodb/testsuites/audit.js index 6635e518a2..6a50844cdf 100644 --- a/js/client/modules/@arangodb/testsuites/audit.js +++ b/js/client/modules/@arangodb/testsuites/audit.js @@ -26,7 +26,8 @@ // ////////////////////////////////////////////////////////////////////////////// const functionsDocumentation = { - 'audit': 'audit log tests' + 'audit_server': 'audit log tests executed on server', + 'audit_client': 'audit log tests executed in client' }; const optionsDocumentation = [ @@ -45,40 +46,41 @@ const RESET = require('internal').COLORS.COLOR_RESET; // const YELLOW = require('internal').COLORS.COLOR_YELLOW; const testPaths = { - audit: [tu.pathForTesting('server/audit')] + audit_server: [tu.pathForTesting('common/audit'), tu.pathForTesting('server/audit')], + audit_client: [tu.pathForTesting('common/audit'), tu.pathForTesting('client/audit')] }; -const sharedConf = { - 'audit.output': 'file://' + fs.getTempFile() -}; - -function auditLog(options) { - if (options.skipAudit === true) { - print('skipping audit log tests!'); - return { - auditLog: { - status: true, - skipped: true +function auditLog(onServer) { + return function(options) { + if (options.skipAudit === true) { + print('skipping audit log tests!'); + return { + auditLog: { + status: true, + skipped: true + } + }; + } + + let opts = { + audit: { + name: 'audit_' + onServer ? 'server' : 'client' } }; - } - - let opts = { - audit: { - name: 'audit', - conf: sharedConf - } + + options.auditLoggingEnabled = true; + + print(CYAN + 'Audit log server tests...' + RESET); + let testCases = tu.scanTestPaths(testPaths['audit_' + (onServer ? 'server' : 'client')]); + + return tu.performTests(options, testCases, 'audit', onServer ? tu.runThere : tu.runInArangosh); }; - - print(CYAN + 'Audit log tests...' + RESET); - let testCases = tu.scanTestPaths(testPaths.audit); - - return tu.performTests(options, testCases, 'audit', tu.runThere, opts.audit.conf); } exports.setup = function (testFns, defaultFns, opts, fnDocs, optionsDoc, allTestPaths) { Object.assign(allTestPaths, testPaths); - testFns['audit'] = auditLog; + testFns['audit_server'] = auditLog(true); + testFns['audit_client'] = auditLog(false); // turn off test by default. opts['skipAudit'] = true; diff --git a/lib/Logger/LogTopic.cpp b/lib/Logger/LogTopic.cpp index 7fc0f6b83d..81adeb3309 100644 --- a/lib/Logger/LogTopic.cpp +++ b/lib/Logger/LogTopic.cpp @@ -146,11 +146,11 @@ LogTopic Logger::VIEWS("views", LogLevel::FATAL); #ifdef USE_ENTERPRISE LogTopic LdapFeature::LDAP("ldap", LogLevel::INFO); -LogTopic AuditFeature::AUDIT_AUTHENTICATION("audit-authentication", LogLevel::INFO); +LogTopic AuditFeature::AUDIT_AUTHENTICATION("audit-authentication", LogLevel::DEBUG); LogTopic AuditFeature::AUDIT_DATABASE("audit-database", LogLevel::INFO); LogTopic AuditFeature::AUDIT_COLLECTION("audit-collection", LogLevel::INFO); LogTopic AuditFeature::AUDIT_VIEW("audit-view", LogLevel::INFO); -LogTopic AuditFeature::AUDIT_DOCUMENT("audit-documentation", LogLevel::INFO); +LogTopic AuditFeature::AUDIT_DOCUMENT("audit-document", LogLevel::DEBUG); LogTopic AuditFeature::AUDIT_SERVICE("audit-service", LogLevel::INFO); #endif