//////////////////////////////////////////////////////////////////////////////// /// @brief vocbase manager /// /// @file /// /// DISCLAIMER /// /// Copyright 2004-2013 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 Dr. Frank Celler /// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "RestServer/VocbaseManager.h" #include "BasicsC/common.h" #include "Logger/Logger.h" #include "Rest/ConnectionInfo.h" #include "Basics/StringUtils.h" #include "BasicsC/files.h" #include "BasicsC/tri-strings.h" #include "RestServer/VocbaseContext.h" #include "VocBase/auth.h" #include "Actions/actions.h" #include "HttpServer/ApplicationEndpointServer.h" #include "V8/JSLoader.h" #include "V8/v8-conv.h" #include "V8/v8-globals.h" #include "V8/v8-utils.h" using namespace std; using namespace triagens::basics; using namespace triagens::arango; using namespace triagens::rest; // ----------------------------------------------------------------------------- // --SECTION-- static // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief the global manager //////////////////////////////////////////////////////////////////////////////// VocbaseManager VocbaseManager::manager; //////////////////////////////////////////////////////////////////////////////// /// @brief add the context to a request //////////////////////////////////////////////////////////////////////////////// bool VocbaseManager::setRequestContext(triagens::rest::HttpRequest* request) { TRI_vocbase_t* vocbase = VocbaseManager::manager.lookupVocbaseByHttpRequest(request); if (vocbase == 0) { // invalid database name specified, database not found etc. return false; } triagens::arango::VocbaseContext* ctx = new triagens::arango::VocbaseContext(request, &VocbaseManager::manager); ctx->setVocbase(vocbase); request->addRequestContext(ctx); return true; } // ----------------------------------------------------------------------------- // --SECTION-- public // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup ArangoDB /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief add system vocbase //////////////////////////////////////////////////////////////////////////////// void VocbaseManager::addSystemVocbase (TRI_vocbase_t* vocbase) { WRITE_LOCKER(_rwLock); _vocbase = vocbase; std::map m; _authCache[vocbase] = m; } //////////////////////////////////////////////////////////////////////////////// /// @brief grab an exclusive lock for creation //////////////////////////////////////////////////////////////////////////////// void VocbaseManager::lockCreation () { _creationLock.lock(); } //////////////////////////////////////////////////////////////////////////////// /// @brief release the exclusive lock for creation //////////////////////////////////////////////////////////////////////////////// void VocbaseManager::unlockCreation () { _creationLock.unlock(); } //////////////////////////////////////////////////////////////////////////////// /// @brief add user vocbase //////////////////////////////////////////////////////////////////////////////// void VocbaseManager::addUserVocbase (TRI_vocbase_t* vocbase) { { WRITE_LOCKER(_rwLock); _vocbases[vocbase->_name] = vocbase; std::map m; _authCache[vocbase] = m; } TRI_ReloadAuthInfo(vocbase); } //////////////////////////////////////////////////////////////////////////////// /// @brief close user vocbases //////////////////////////////////////////////////////////////////////////////// void VocbaseManager::closeUserVocbases () { WRITE_LOCKER(_rwLock); std::map::iterator i = _vocbases.begin(); for (; i != _vocbases.end(); ++i) { TRI_vocbase_t* vocbase = (*i).second; TRI_DestroyVocBase(vocbase, 0); TRI_Free(TRI_UNKNOWN_MEM_ZONE, vocbase); } _vocbases.clear(); } //////////////////////////////////////////////////////////////////////////////// /// @brief look up vocbase by name //////////////////////////////////////////////////////////////////////////////// TRI_vocbase_t* VocbaseManager::lookupVocbaseByName (string const& name) { if (name == TRI_VOC_SYSTEM_DATABASE) { return _vocbase; } READ_LOCKER(_rwLock); map::iterator find = _vocbases.find(name); if (find != _vocbases.end()) { return find->second; } return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief check if name and path are not used //////////////////////////////////////////////////////////////////////////////// int VocbaseManager::canAddVocbase (std::string const& name, std::string const& path, bool checkPath) { if (! isValidName(name)) { return TRI_ERROR_ARANGO_DATABASE_NAME_INVALID; } if (path.empty()) { return TRI_ERROR_ARANGO_DATABASE_PATH_INVALID; } // loop over all vocbases and check name and path READ_LOCKER(_rwLock); // system vocbase if (name == string(_vocbase->_name)) { return TRI_ERROR_ARANGO_DATABASE_NAME_USED; } if (path == string(_vocbase->_path)) { return TRI_ERROR_ARANGO_DATABASE_PATH_USED; } // user vocbases std::map::iterator i = _vocbases.begin(); for (; i != _vocbases.end(); ++i) { TRI_vocbase_t* vocbase = i->second; if (name == string(vocbase->_name)) { return TRI_ERROR_ARANGO_DATABASE_NAME_USED; } if (path == string(vocbase->_path)) { return TRI_ERROR_ARANGO_DATABASE_PATH_USED; } } // check if the path already exists if (checkPath && TRI_ExistsFile(path.c_str())) { return TRI_ERROR_ARANGO_DATABASE_PATH_USED; } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief check if a collection name is valid //////////////////////////////////////////////////////////////////////////////// bool VocbaseManager::isValidName (std::string const& name) const { return TRI_IsAllowedDatabaseName(false, name.c_str()); } //////////////////////////////////////////////////////////////////////////////// /// @brief remove a vocbase by name //////////////////////////////////////////////////////////////////////////////// int VocbaseManager::deleteVocbase (string const& name) { if (name == TRI_VOC_SYSTEM_DATABASE) { // cannot delete _system database return TRI_ERROR_FORBIDDEN; } TRI_vocbase_t* vocbase = 0; { WRITE_LOCKER(_rwLock); map::iterator find = _vocbases.find(name); if (find == _vocbases.end()) { // database does not exist return TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND; } // ok, we found it vocbase = (*find).second; // remove from the list _vocbases.erase(name); } assert(vocbase != 0); const string path = string(vocbase->_path); LOGGER_INFO("removing database '" << name << "', path '" << path << "'"); // destroy the vocbase, but not its collections // there might be JavaScript objects referencing the collections somehow, // so we must not free them yet // putting the collections in this vector will allow us freeing them later TRI_DestroyVocBase(vocbase, _freeCollections); // same for the vocbase _freeVocbases.push_back(vocbase); // ok, now delete the data in the file system int res = TRI_RemoveDirectory(path.c_str()); return res; } //////////////////////////////////////////////////////////////////////////////// /// @brief run version check /// @return bool returns false if the version check fails //////////////////////////////////////////////////////////////////////////////// bool VocbaseManager::runVersionCheck (TRI_vocbase_t* vocbase, v8::Handle context) { if (! _startupLoader) { LOGGER_ERROR("Javascript start up loader not found."); return false; } v8::HandleScope scope; TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); TRI_vocbase_t* orig = (TRI_vocbase_t*) v8g->_vocbase; v8g->_vocbase = vocbase; v8::Handle result = _startupLoader->executeGlobalScript(context, "server/version-check.js"); v8g->_vocbase = orig; return TRI_ObjectToBoolean(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief initialize foxx //////////////////////////////////////////////////////////////////////////////// void VocbaseManager::initializeFoxx (TRI_vocbase_t* vocbase, v8::Handle context) { TRI_vocbase_t* orig = 0; { v8::HandleScope scope; TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); orig = (TRI_vocbase_t*) v8g->_vocbase; v8g->_vocbase = vocbase; } v8::HandleScope scope; TRI_ExecuteJavaScriptString(context, v8::String::New("require(\"internal\").initializeFoxx()"), v8::String::New("initialize foxx"), false); { v8::HandleScope scope; TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); v8g->_vocbase = orig; } } //////////////////////////////////////////////////////////////////////////////// /// @brief add an endpoint //////////////////////////////////////////////////////////////////////////////// bool VocbaseManager::addEndpoint (std::string const& name, std::vector const& databaseNames) { if (_endpointServer) { { WRITE_LOCKER(_rwLock); _endpoints[name] = databaseNames; } return _endpointServer->addEndpoint(name); } return false; } //////////////////////////////////////////////////////////////////////////////// /// @brief look up vocbase by http request //////////////////////////////////////////////////////////////////////////////// TRI_vocbase_t* VocbaseManager::lookupVocbaseByHttpRequest (triagens::rest::HttpRequest* request) { TRI_vocbase_t* vocbase = 0; // get database name from request string requestedName = request->databaseName(); if (requestedName.empty()) { // no name set in request, use system database name as a fallback requestedName = TRI_VOC_SYSTEM_DATABASE; vocbase = _vocbase; } else if (requestedName == TRI_VOC_SYSTEM_DATABASE) { vocbase = _vocbase; } READ_LOCKER(_rwLock); // check if we have a database with the requested name // this only needs to be done for non-system databases if (vocbase == 0) { map::iterator it = _vocbases.find(requestedName); if (it == _vocbases.end()) { // requested database does not exist return 0; } vocbase = (*it).second; } assert(vocbase != 0); // check if we have an endpoint ConnectionInfo ci = request->connectionInfo(); const string& endpoint = ci.endpoint; map >::const_iterator it2 = _endpoints.find(endpoint); if (it2 == _endpoints.end()) { // no user mapping entered for the endpoint. return the requested database return vocbase; } // we have a user-defined mapping for the endpoint const vector& databaseNames = (*it2).second; if (databaseNames.size() == 0) { // list of database names is specified but empty. this means no-one will get access return 0; } // finally check if the requested database is in the list of allowed databases for the endpoint vector::const_iterator it3; for (it3 = databaseNames.begin(); it3 != databaseNames.end(); ++it3) { if (requestedName == *it3) { return vocbase; } } // requested database not available for the endpoint return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief authenticate a request //////////////////////////////////////////////////////////////////////////////// HttpResponse::HttpResponseCode VocbaseManager::authenticate (TRI_vocbase_t* vocbase, triagens::rest::HttpRequest* request) { assert(vocbase != 0); std::map >::iterator mapIter; bool found; char const* auth = request->header("authorization", found); if (! found || ! TRI_CaseEqualString2(auth, "basic ", 6)) { return HttpResponse::UNAUTHORIZED; } // skip over "basic " auth += 6; while (*auth == ' ') { ++auth; } { READ_LOCKER(_rwLock); mapIter = _authCache.find(vocbase); if (mapIter == _authCache.end()) { // unknown vocbase return HttpResponse::NOT_FOUND; } map::iterator i = mapIter->second.find(auth); if (i != mapIter->second.end()) { request->setUser(i->second); return HttpResponse::OK; } } string up = StringUtils::decodeBase64(auth); std::string::size_type n = up.find(':', 0); if (n == std::string::npos || n == 0 || n + 1 > up.size()) { LOGGER_TRACE("invalid authentication data found, cannot extract username/password"); return HttpResponse::BAD; } const string username = up.substr(0, n); LOGGER_TRACE("checking authentication for user '" << username << "'"); bool res = TRI_CheckAuthenticationAuthInfo2(vocbase, username.c_str(), up.substr(n + 1).c_str()); if (! res) { return HttpResponse::UNAUTHORIZED; } WRITE_LOCKER(_rwLock); mapIter = _authCache.find(vocbase); if (mapIter == _authCache.end()) { // unknown vocbase return HttpResponse::UNAUTHORIZED; } mapIter->second[auth] = username; request->setUser(username); // TODO: create a user object for the VocbaseContext return HttpResponse::OK; } //////////////////////////////////////////////////////////////////////////////// /// @brief reload auth info //////////////////////////////////////////////////////////////////////////////// bool VocbaseManager::reloadAuthInfo (TRI_vocbase_t* vocbase) { std::map >::iterator mapIter; { WRITE_LOCKER(_rwLock); mapIter = _authCache.find(vocbase); if (mapIter != _authCache.end()) { mapIter->second.clear(); } } return TRI_ReloadAuthInfo(vocbase); } //////////////////////////////////////////////////////////////////////////////// /// @brief get list of database names //////////////////////////////////////////////////////////////////////////////// vector VocbaseManager::vocbases () { vector result; result.push_back(_vocbase); READ_LOCKER(_rwLock); std::map::iterator i = _vocbases.begin(); for (; i != _vocbases.end(); ++i) { result.push_back(i->second); } return result; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: