From 21360bfc25798135e05be6308b210d0fc167c739 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Tue, 5 Nov 2013 12:42:50 +0100 Subject: [PATCH 1/4] free json in case of error --- arangod/RestHandler/RestEdgeHandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arangod/RestHandler/RestEdgeHandler.cpp b/arangod/RestHandler/RestEdgeHandler.cpp index a3a0305523..41b0af9498 100644 --- a/arangod/RestHandler/RestEdgeHandler.cpp +++ b/arangod/RestHandler/RestEdgeHandler.cpp @@ -249,6 +249,8 @@ bool RestEdgeHandler::createDocument () { if (res != TRI_ERROR_NO_ERROR) { FREE_STRING(TRI_CORE_MEM_ZONE, edge._fromKey); FREE_STRING(TRI_CORE_MEM_ZONE, edge._toKey); + + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); if (res == TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND) { generateError(HttpResponse::NOT_FOUND, res, wrongPart + " does not point to a valid collection"); From 8382e5dbb63dadbd5d421ee7a588c3f6cbdf0cd0 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Mon, 4 Nov 2013 17:43:57 +0100 Subject: [PATCH 2/4] flush authentication cache --- arangod/V8Server/v8-vocbase.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 21af738d65..a351cfcd29 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -8005,6 +8005,9 @@ static v8::Handle JS_CreateDatabase (v8::Arguments const& argv) { // version check failed // TODO: report an error } + + // populate the authentication cache. otherwise no one can access the new database + TRI_ReloadAuthInfo(database); // finally decrease the reference-counter TRI_ReleaseVocBase(database); From d30ca7b658af1a8ddbf42742a97b2ae1008724c1 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Mon, 4 Nov 2013 14:22:00 +0100 Subject: [PATCH 3/4] added `/_api/database/user` method --- .../ImplementorManual/HttpDatabase.md | 4 + .../ImplementorManual/HttpDatabaseTOC.md | 1 + UnitTests/Makefile.unittests | 1 + arangod/V8Server/v8-vocbase.cpp | 20 ++- arangod/VocBase/auth.c | 2 +- arangod/VocBase/server.c | 93 ++++++++++++- arangod/VocBase/server.h | 9 ++ arangod/VocBase/vocbase.c | 2 + js/actions/api-database.js | 74 +++++++++-- .../frontend/js/bootstrap/module-internal.js | 19 +++ js/common/bootstrap/module-internal.js | 19 +++ js/common/tests/shell-base64.js | 123 ++++++++++++++++++ lib/V8/v8-utils.cpp | 60 ++++++++- 13 files changed, 406 insertions(+), 21 deletions(-) create mode 100644 js/common/tests/shell-base64.js diff --git a/Documentation/ImplementorManual/HttpDatabase.md b/Documentation/ImplementorManual/HttpDatabase.md index dd77deb1a7..99919bd8dc 100644 --- a/Documentation/ImplementorManual/HttpDatabase.md +++ b/Documentation/ImplementorManual/HttpDatabase.md @@ -82,6 +82,10 @@ Managing Databases using HTTP {#HttpDatabaseHttp} @anchor HttpDatabaseCurrent @copydetails JSF_get_api_database_current +@CLEARPAGE +@anchor HttpDatabaseUser +@copydetails JSF_get_api_database_user + @CLEARPAGE @anchor HttpDatabaseList @copydetails JSF_get_api_database_list diff --git a/Documentation/ImplementorManual/HttpDatabaseTOC.md b/Documentation/ImplementorManual/HttpDatabaseTOC.md index 0b04dfa08e..6d856bc344 100644 --- a/Documentation/ImplementorManual/HttpDatabaseTOC.md +++ b/Documentation/ImplementorManual/HttpDatabaseTOC.md @@ -7,6 +7,7 @@ TOC {#HttpDatabaseTOC} - @ref HttpDatabaseManagement - @ref HttpDatabaseHttp - @ref HttpDatabaseCurrent "GET /_api/database/current" + - @ref HttpDatabaseUser "GET /_api/database/user" - @ref HttpDatabaseList "GET /_api/database" - @ref HttpDatabaseCreate "POST /_api/database" - @ref HttpDatabaseDelete "DELETE /_api/database/database-name" diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 9c2f3c771c..6232cc3b6d 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -284,6 +284,7 @@ SHELL_COMMON = \ @top_srcdir@/js/common/tests/shell-require.js \ @top_srcdir@/js/common/tests/shell-aqlfunctions.js \ @top_srcdir@/js/common/tests/shell-attributes.js \ + @top_srcdir@/js/common/tests/shell-base64.js \ @top_srcdir@/js/common/tests/shell-collection.js \ @top_srcdir@/js/common/tests/shell-collection-volatile.js \ @top_srcdir@/js/common/tests/shell-crypto.js \ diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index a351cfcd29..99efab1352 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -7866,7 +7866,8 @@ static v8::Handle JS_UseDatabase (v8::Arguments const& argv) { static v8::Handle JS_ListDatabases (v8::Arguments const& argv) { v8::HandleScope scope; - if (argv.Length() != 0) { + const uint32_t argc = argv.Length(); + if (argc != 0 && argc != 2) { TRI_V8_EXCEPTION_USAGE(scope, "db._listDatabases()"); } @@ -7876,7 +7877,8 @@ static v8::Handle JS_ListDatabases (v8::Arguments const& argv) { TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } - if (! TRI_IsSystemVocBase(vocbase)) { + if (argc == 0 && + ! TRI_IsSystemVocBase(vocbase)) { TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); } @@ -7884,7 +7886,19 @@ static v8::Handle JS_ListDatabases (v8::Arguments const& argv) { TRI_vector_string_t names; TRI_InitVectorString(&names, TRI_UNKNOWN_MEM_ZONE); - int res = TRI_GetDatabaseNamesServer((TRI_server_t*) v8g->_server, &names); + + int res; + + if (argc == 0) { + // return all databases + res = TRI_GetDatabaseNamesServer((TRI_server_t*) v8g->_server, &names); + } + else { + // return all databases for a specific user + string username = TRI_ObjectToString(argv[0]); + string password = TRI_ObjectToString(argv[1]); + res = TRI_GetUserDatabasesServer((TRI_server_t*) v8g->_server, username.c_str(), password.c_str(), &names); + } if (res != TRI_ERROR_NO_ERROR) { TRI_DestroyVectorString(&names); diff --git a/arangod/VocBase/auth.c b/arangod/VocBase/auth.c index c4e152644a..3670fd090c 100644 --- a/arangod/VocBase/auth.c +++ b/arangod/VocBase/auth.c @@ -567,7 +567,7 @@ bool TRI_CheckAuthenticationAuthInfo (TRI_vocbase_t* vocbase, TRI_ReadUnlockReadWriteLock(&vocbase->_authInfoLock); - if (res) { + if (res && hash != NULL) { // insert item into the cache TRI_vocbase_auth_cache_t* cached; diff --git a/arangod/VocBase/server.c b/arangod/VocBase/server.c index 6f03479969..9c008e7617 100644 --- a/arangod/VocBase/server.c +++ b/arangod/VocBase/server.c @@ -43,6 +43,7 @@ #include "BasicsC/random.h" #include "BasicsC/tri-strings.h" #include "Ahuacatl/ahuacatl-statementlist.h" +#include "VocBase/auth.h" #include "VocBase/vocbase.h" // ----------------------------------------------------------------------------- @@ -474,9 +475,39 @@ static int WriteShutdownInfo (TRI_server_t* server) { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief returns the current tick value, without using a lock +/// @brief check if a user can see a database //////////////////////////////////////////////////////////////////////////////// +static bool CanUseDatabase (TRI_vocbase_t* vocbase, + char const* username, + char const* password) { + if (! vocbase->_settings.requireAuthentication) { + // authentication is turned off + return true; + } + + return TRI_CheckAuthenticationAuthInfo(vocbase, NULL, username, password); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief comparator for database names +//////////////////////////////////////////////////////////////////////////////// + +static int DatabaseNameComparator (const void* lhs, const void* rhs) { + const char* l = *((char**) lhs); + const char* r = *((char**) rhs); + + return strcmp(l, r); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief sort a list of database names +//////////////////////////////////////////////////////////////////////////////// + +static void SortDatabaseNames (TRI_vector_string_t* names) { + qsort(names->_buffer, names->_length, sizeof(char*), &DatabaseNameComparator); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief extract the numeric part from a filename //////////////////////////////////////////////////////////////////////////////// @@ -498,7 +529,7 @@ static uint64_t GetNumericFilenamePart (const char* filename) { /// the filename. this is used to sort database filenames on startup //////////////////////////////////////////////////////////////////////////////// -static int NameComparator (const void* lhs, const void* rhs) { +static int DatabaseIdComparator (const void* lhs, const void* rhs) { const char* l = *((char**) lhs); const char* r = *((char**) rhs); @@ -600,7 +631,7 @@ static int OpenDatabases (TRI_server_t* server) { // open databases in defined order if (n > 1) { - qsort(files._buffer, n, sizeof(char**), &NameComparator); + qsort(files._buffer, n, sizeof(char**), &DatabaseIdComparator); } for (i = 0; i < n; ++i) { @@ -914,7 +945,7 @@ static int GetDatabases (TRI_server_t* server, regfree(&re); // sort by id - qsort(databases->_buffer, databases->_length, sizeof(char*), &NameComparator); + qsort(databases->_buffer, databases->_length, sizeof(char*), &DatabaseIdComparator); return res; } @@ -2178,6 +2209,58 @@ void TRI_ReleaseDatabaseServer (TRI_server_t* server, TRI_ReleaseVocBase(vocbase); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the list of all databases a user can see +//////////////////////////////////////////////////////////////////////////////// + +int TRI_GetUserDatabasesServer (TRI_server_t* server, + char const* username, + char const* password, + TRI_vector_string_t* names) { + + size_t i, n; + int res; + + res = TRI_ERROR_NO_ERROR; + + TRI_ReadLockReadWriteLock(&server->_databasesLock); + n = server->_databases._nrAlloc; + + for (i = 0; i < n; ++i) { + TRI_vocbase_t* vocbase = server->_databases._table[i]; + + if (vocbase != NULL) { + char* copy; + + assert(vocbase->_name != NULL); + + if (! CanUseDatabase(vocbase, username, password)) { + // user cannot see database + continue; + } + + copy = TRI_DuplicateStringZ(names->_memoryZone, vocbase->_name); + + if (copy == NULL) { + res = TRI_ERROR_OUT_OF_MEMORY; + break; + } + + if (TRI_PushBackVectorString(names, copy) != TRI_ERROR_NO_ERROR) { + // insertion failed. + TRI_Free(names->_memoryZone, copy); + res = TRI_ERROR_OUT_OF_MEMORY; + break; + } + } + } + TRI_ReadUnlockReadWriteLock(&server->_databasesLock); + + SortDatabaseNames(names); + + return res; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief return the list of all database names //////////////////////////////////////////////////////////////////////////////// @@ -2217,6 +2300,8 @@ int TRI_GetDatabaseNamesServer (TRI_server_t* server, } } TRI_ReadUnlockReadWriteLock(&server->_databasesLock); + + SortDatabaseNames(names); return res; } diff --git a/arangod/VocBase/server.h b/arangod/VocBase/server.h index b22061a8e1..bcd73cbc11 100644 --- a/arangod/VocBase/server.h +++ b/arangod/VocBase/server.h @@ -207,6 +207,15 @@ struct TRI_vocbase_s* TRI_UseDatabaseServer (TRI_server_t*, void TRI_ReleaseDatabaseServer (TRI_server_t*, struct TRI_vocbase_s*); +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the list of all databases a user can see +//////////////////////////////////////////////////////////////////////////////// + +int TRI_GetUserDatabasesServer (TRI_server_t*, + char const*, + char const*, + TRI_vector_string_t*); + //////////////////////////////////////////////////////////////////////////////// /// @brief return the list of all database names //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/VocBase/vocbase.c b/arangod/VocBase/vocbase.c index b21e45fc91..a439748830 100644 --- a/arangod/VocBase/vocbase.c +++ b/arangod/VocBase/vocbase.c @@ -1374,6 +1374,8 @@ TRI_vocbase_t* TRI_OpenVocBase (TRI_server_t* server, } + TRI_ReloadAuthInfo(vocbase); + // ............................................................................. // vocbase is now active // ............................................................................. diff --git a/js/actions/api-database.js b/js/actions/api-database.js index dfef8aadc0..031b01ed48 100644 --- a/js/actions/api-database.js +++ b/js/actions/api-database.js @@ -49,7 +49,7 @@ var API = "_api/database"; /// @RESTHEADER{GET /_api/database,retrieves a list of all existing databases} /// /// @RESTDESCRIPTION -/// Retrieves a list of all existing databases +/// Retrieves the list of all existing databases /// /// Note: retrieving the list of databases is only possible from within the `_system` database. /// @@ -64,9 +64,6 @@ var API = "_api/database"; /// @RESTRETURNCODE{403} /// is returned if the request was not executed in the `_system` database. /// -/// @RESTRETURNCODE{404} -/// is returned if the database could not be found. -/// /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_RUN{RestDatabaseGet} @@ -79,6 +76,36 @@ var API = "_api/database"; /// @END_EXAMPLE_ARANGOSH_RUN //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @fn JSF_get_api_database_user +/// @brief retrieves a list of all databases the current user can access +/// +/// @RESTHEADER{GET /_api/database/user,retrieves a list of all databases the current user can access} +/// +/// @RESTDESCRIPTION +/// Retrieves the list of all databases the current user can access without +/// specifying a different username or password. +/// +/// @RESTRETURNCODES +/// +/// @RESTRETURNCODE{200} +/// is returned if the list of database was compiled successfully. +/// +/// @RESTRETURNCODE{400} +/// is returned if the request is invalid. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_RUN{RestDatabaseGetUser} +/// var url = "/_api/database/user"; +/// var response = logCurlRequest('GET', url); +/// +/// assert(response.code === 200); +/// +/// logJsonResponse(response); +/// @END_EXAMPLE_ARANGOSH_RUN +//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// /// @fn JSF_get_api_database_current /// @brief retrieves information about the current database @@ -127,7 +154,7 @@ function get_api_database (req, res) { return; } - if (req.suffix.length === 1 && req.suffix[0] !== 'current') { + if (req.suffix.length > 1) { actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER); return; } @@ -138,13 +165,36 @@ function get_api_database (req, res) { result = arangodb.db._listDatabases(); } else { - // information about the current database - result = { - name: arangodb.db._name(), - id: arangodb.db._id(), - path: arangodb.db._path(), - isSystem: arangodb.db._isSystem() - }; + if (req.suffix[0] === 'user') { + // return all databases for current user + var username = '', password = ''; + + if (req.headers.hasOwnProperty('authorization')) { + var header = req.headers.authorization.replace(/^Basic\s+/i, ''); + var decoded = require("internal").base64Decode(header); + var pos = decoded.indexOf(':'); + + if (pos >= 0) { + username = decoded.substr(0, pos); + password = decoded.substr(pos + 1, decoded.length - pos - 1); + } + } + + result = arangodb.db._listDatabases(username, password); + } + else if (req.suffix[0] === 'current') { + // information about the current database + result = { + name: arangodb.db._name(), + id: arangodb.db._id(), + path: arangodb.db._path(), + isSystem: arangodb.db._isSystem() + }; + } + else { + actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER); + return; + } } actions.resultOk(req, res, actions.HTTP_OK, { result : result }); diff --git a/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js b/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js index a860fbb7fb..18f8cd021b 100644 --- a/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js +++ b/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js @@ -5,6 +5,7 @@ REPLICATION_LOGGER_CONFIGURE, REPLICATION_APPLIER_CONFIGURE, REPLICATION_APPLIER_START, REPLICATION_APPLIER_STOP, REPLICATION_APPLIER_FORGET, REPLICATION_APPLIER_STATE, REPLICATION_SYNCHRONISE, REPLICATION_SERVER_ID, CONFIGURE_ENDPOINT, REMOVE_ENDPOINT, LIST_ENDPOINTS, + SYS_BASE64DECODE, SYS_BASE64ENCODE, SYS_DEBUG_CAN_USE_FAILAT, SYS_DEBUG_SET_FAILAT, SYS_DEBUG_REMOVE_FAILAT, SYS_DEBUG_CLEAR_FAILAT, SYS_DOWNLOAD, SYS_EXECUTE, SYS_LOAD, SYS_LOG_LEVEL, SYS_MD5, SYS_OUTPUT, SYS_PROCESS_STATISTICS, SYS_RAND, SYS_SERVER_STATISTICS, SYS_SPRINTF, SYS_TIME, SYS_START_PAGER, SYS_STOP_PAGER, @@ -356,6 +357,24 @@ delete LIST_ENDPOINTS; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief base64Decode +//////////////////////////////////////////////////////////////////////////////// + + if (typeof SYS_BASE64DECODE !== "undefined") { + exports.base64Decode = SYS_BASE64DECODE; + delete SYS_BASE64DECODE; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief base64Encode +//////////////////////////////////////////////////////////////////////////////// + + if (typeof SYS_BASE64ENCODE !== "undefined") { + exports.base64Encode = SYS_BASE64ENCODE; + delete SYS_BASE64ENCODE; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief debugSetFailAt //////////////////////////////////////////////////////////////////////////////// diff --git a/js/common/bootstrap/module-internal.js b/js/common/bootstrap/module-internal.js index a860fbb7fb..18f8cd021b 100644 --- a/js/common/bootstrap/module-internal.js +++ b/js/common/bootstrap/module-internal.js @@ -5,6 +5,7 @@ REPLICATION_LOGGER_CONFIGURE, REPLICATION_APPLIER_CONFIGURE, REPLICATION_APPLIER_START, REPLICATION_APPLIER_STOP, REPLICATION_APPLIER_FORGET, REPLICATION_APPLIER_STATE, REPLICATION_SYNCHRONISE, REPLICATION_SERVER_ID, CONFIGURE_ENDPOINT, REMOVE_ENDPOINT, LIST_ENDPOINTS, + SYS_BASE64DECODE, SYS_BASE64ENCODE, SYS_DEBUG_CAN_USE_FAILAT, SYS_DEBUG_SET_FAILAT, SYS_DEBUG_REMOVE_FAILAT, SYS_DEBUG_CLEAR_FAILAT, SYS_DOWNLOAD, SYS_EXECUTE, SYS_LOAD, SYS_LOG_LEVEL, SYS_MD5, SYS_OUTPUT, SYS_PROCESS_STATISTICS, SYS_RAND, SYS_SERVER_STATISTICS, SYS_SPRINTF, SYS_TIME, SYS_START_PAGER, SYS_STOP_PAGER, @@ -356,6 +357,24 @@ delete LIST_ENDPOINTS; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief base64Decode +//////////////////////////////////////////////////////////////////////////////// + + if (typeof SYS_BASE64DECODE !== "undefined") { + exports.base64Decode = SYS_BASE64DECODE; + delete SYS_BASE64DECODE; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief base64Encode +//////////////////////////////////////////////////////////////////////////////// + + if (typeof SYS_BASE64ENCODE !== "undefined") { + exports.base64Encode = SYS_BASE64ENCODE; + delete SYS_BASE64ENCODE; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief debugSetFailAt //////////////////////////////////////////////////////////////////////////////// diff --git a/js/common/tests/shell-base64.js b/js/common/tests/shell-base64.js new file mode 100644 index 0000000000..cfacaa7832 --- /dev/null +++ b/js/common/tests/shell-base64.js @@ -0,0 +1,123 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief test the base64 functions +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-2012 triagens GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Jan Steemann +/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var jsunity = require("jsunity"); +var internal = require("internal"); + +// ----------------------------------------------------------------------------- +// --SECTION-- base64 functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite +//////////////////////////////////////////////////////////////////////////////// + +function Base64Suite () { + return { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test encode +//////////////////////////////////////////////////////////////////////////////// + + testBase64Encode : function () { + var data = [ + ["",""], + [" ","IA=="], + [" ","ICA="], + ["\nnew line\n","Cm5ldyBsaW5lCg=="], + ["abc","YWJj"], + ["ABC","QUJD"], + ["abC","YWJD"], + [" aBC","IGFCQw=="], + ["123","MTIz"], + ["abcdef123456","YWJjZGVmMTIzNDU2"], + ["the Quick brown fox jumped over the lazy dog","dGhlIFF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2c="], + ["This is a long string that contains a lot of characters. It should work without any particular problems, too.","VGhpcyBpcyBhIGxvbmcgc3RyaW5nIHRoYXQgY29udGFpbnMgYSBsb3Qgb2YgY2hhcmFjdGVycy4gSXQgc2hvdWxkIHdvcmsgd2l0aG91dCBhbnkgcGFydGljdWxhciBwcm9ibGVtcywgdG9vLg=="], + [1,"MQ=="], + [2,"Mg=="], + [-1,"LTE="], + [100,"MTAw"], + [1000,"MTAwMA=="], + [99,"OTk="] + ]; + + data.forEach(function (value) { + assertEqual(value[1], internal.base64Encode(value[0])); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test decode +//////////////////////////////////////////////////////////////////////////////// + + testBase64Decode : function () { + var data = [ + ["",""], + [" ","IA=="], + [" ","ICA="], + ["\nnew line\n","Cm5ldyBsaW5lCg=="], + ["abc","YWJj"], + ["ABC","QUJD"], + ["abC","YWJD"], + [" aBC","IGFCQw=="], + ["123","MTIz"], + ["abcdef123456","YWJjZGVmMTIzNDU2"], + ["the Quick brown fox jumped over the lazy dog","dGhlIFF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2c="], + ["This is a long string that contains a lot of characters. It should work without any particular problems, too.","VGhpcyBpcyBhIGxvbmcgc3RyaW5nIHRoYXQgY29udGFpbnMgYSBsb3Qgb2YgY2hhcmFjdGVycy4gSXQgc2hvdWxkIHdvcmsgd2l0aG91dCBhbnkgcGFydGljdWxhciBwcm9ibGVtcywgdG9vLg=="], + ["1","MQ=="], + ["2","Mg=="], + ["-1","LTE="], + ["100","MTAw"], + ["1000","MTAwMA=="], + ["99","OTk="] + ]; + + data.forEach(function (value) { + assertEqual(value[0], internal.base64Decode(value[1])); + }); + } + + }; +} + +// ----------------------------------------------------------------------------- +// --SECTION-- main +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the test suite +//////////////////////////////////////////////////////////////////////////////// + +jsunity.run(Base64Suite); + +return jsunity.done(); + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: + diff --git a/lib/V8/v8-utils.cpp b/lib/V8/v8-utils.cpp index 75014adf6c..3b8dae8e24 100644 --- a/lib/V8/v8-utils.cpp +++ b/lib/V8/v8-utils.cpp @@ -283,6 +283,62 @@ static void FillDistribution (v8::Handle list, // --SECTION-- JS functions // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief decode a base64-encoded string +/// +/// @FUN{internal.base64Decode(@FA{value})} +/// +/// Base64-decodes the string @FA{value}. +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_Base64Decode (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() != 1) { + TRI_V8_EXCEPTION_USAGE(scope, "base64Decode()"); + } + + string base64; + + try { + string value = TRI_ObjectToString(argv[0]); + base64 = StringUtils::decodeBase64(value); + } + catch (...) { + TRI_V8_EXCEPTION_MESSAGE(scope, TRI_errno(), TRI_last_error()); + } + + return scope.Close(v8::String::New(base64.c_str(), base64.size())); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief base64-encode a string +/// +/// @FUN{internal.base64Encode(@FA{value})} +/// +/// Base64-encodes the string @FA{value}. +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_Base64Encode (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() != 1) { + TRI_V8_EXCEPTION_USAGE(scope, "base64Encode()"); + } + + string base64; + + try { + string value = TRI_ObjectToString(argv[0]); + base64 = StringUtils::encodeBase64(value); + } + catch (...) { + TRI_V8_EXCEPTION_MESSAGE(scope, TRI_errno(), TRI_last_error()); + } + + return scope.Close(v8::String::New(base64.c_str(), base64.size())); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief parse a Javascript snippet, but do not execute it /// @@ -1709,7 +1765,7 @@ static v8::Handle JS_Read64 (v8::Arguments const& argv) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_errno(), TRI_last_error()); } - return scope.Close(v8::String::New(base64.c_str())); + return scope.Close(v8::String::New(base64.c_str(), base64.size())); } //////////////////////////////////////////////////////////////////////////////// @@ -2700,6 +2756,8 @@ void TRI_InitV8Utils (v8::Handle context, TRI_AddGlobalFunctionVocbase(context, "FS_UNZIP_FILE", JS_UnzipFile); TRI_AddGlobalFunctionVocbase(context, "FS_ZIP_FILE", JS_ZipFile); + TRI_AddGlobalFunctionVocbase(context, "SYS_BASE64DECODE", JS_Base64Decode); + TRI_AddGlobalFunctionVocbase(context, "SYS_BASE64ENCODE", JS_Base64Encode); TRI_AddGlobalFunctionVocbase(context, "SYS_DOWNLOAD", JS_Download); TRI_AddGlobalFunctionVocbase(context, "SYS_EXECUTE", JS_Execute); TRI_AddGlobalFunctionVocbase(context, "SYS_GETLINE", JS_Getline); From 1d8d26b7abd1d9423e6f511c5b000ba2fb532c42 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Mon, 4 Nov 2013 14:23:30 +0100 Subject: [PATCH 4/4] added test --- UnitTests/HttpInterface/api-database-spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/UnitTests/HttpInterface/api-database-spec.rb b/UnitTests/HttpInterface/api-database-spec.rb index cb6d85ba51..bd3119ec43 100644 --- a/UnitTests/HttpInterface/api-database-spec.rb +++ b/UnitTests/HttpInterface/api-database-spec.rb @@ -22,6 +22,19 @@ describe ArangoDB do result.should include("_system") end +################################################################################ +## retrieving the list of databases for the current user +################################################################################ + + it "retrieves the list of user-specific databases" do + doc = ArangoDB.log_get("#{prefix}-list-user", api + "/user") + + doc.code.should eq(200) + result = doc.parsed_response["result"] + + result.should include("_system") + end + ################################################################################ ## checking information about current database ################################################################################