//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 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 ArangoDB GmbH, Cologne, Germany /// /// @author Dr. Frank Celler //////////////////////////////////////////////////////////////////////////////// #include "AuthInfo.h" #include #include #include #include "Basics/ReadLocker.h" #include "Basics/VelocyPackHelper.h" #include "Basics/WriteLocker.h" #include "Logger/Logger.h" #include "RestServer/DatabaseFeature.h" #include "Utils/SingleCollectionTransaction.h" #include "Utils/StandaloneTransactionContext.h" #include "VocBase/MasterPointer.h" #include "VocBase/collection.h" using namespace arangodb; using namespace arangodb::basics; using namespace arangodb::velocypack; static AuthEntry CreateAuthEntry(VPackSlice const& slice) { if (slice.isNone() || !slice.isObject()) { return AuthEntry(); } // extract "user" attribute VPackSlice const userSlice = slice.get("user"); if (!userSlice.isString()) { LOG(DEBUG) << "cannot extract username"; return AuthEntry(); } VPackSlice const authDataSlice = slice.get("authData"); if (!authDataSlice.isObject()) { LOG(DEBUG) << "cannot extract authData"; return AuthEntry(); } VPackSlice const simpleSlice = authDataSlice.get("simple"); if (!simpleSlice.isObject()) { LOG(DEBUG) << "cannot extract simple"; return AuthEntry(); } VPackSlice const methodSlice = simpleSlice.get("method"); VPackSlice const saltSlice = simpleSlice.get("salt"); VPackSlice const hashSlice = simpleSlice.get("hash"); if (!methodSlice.isString() || !saltSlice.isString() || !hashSlice.isString()) { LOG(DEBUG) << "cannot extract password internals"; return AuthEntry(); } // extract "active" attribute bool active; VPackSlice const activeSlice = authDataSlice.get("active"); if (!activeSlice.isBoolean()) { LOG(DEBUG) << "cannot extract active flag"; return AuthEntry(); } active = activeSlice.getBool(); // extract "changePassword" attribute bool mustChange = VelocyPackHelper::getBooleanValue(slice, "changePassword", false); std::cout << "user: " << userSlice.copyString() << "\n" << "method: " << methodSlice.copyString() << "\n" << "salt: " << saltSlice.copyString() << "\n" << "hash: " << hashSlice.copyString() << "\n" << "active: " << active << "\n" << "must change: " << mustChange << "\n"; return AuthEntry(userSlice.copyString(), methodSlice.copyString(), saltSlice.copyString(), hashSlice.copyString(), active, mustChange); } void AuthInfo::clear() { _authInfo.clear(); _authCache.clear(); } bool AuthInfo::reload() { insertInitial(); TRI_vocbase_t* vocbase = DatabaseFeature::DATABASE->vocbase(); if (vocbase == nullptr) { LOG(DEBUG) << "system database is unknown, cannot load authentication " << "and authorization information"; return false; } LOG(DEBUG) << "starting to load authentication and authorization information"; WRITE_LOCKER(writeLocker, _authInfoLock); SingleCollectionTransaction trx(StandaloneTransactionContext::Create(vocbase), TRI_COL_NAME_USERS, TRI_TRANSACTION_READ); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { return false; } OperationResult users = trx.all(TRI_COL_NAME_USERS, 0, UINT64_MAX, OperationOptions()); trx.finish(users.code); if (users.failed()) { LOG(ERR) << "cannot read users from _users collection"; return false; } auto usersSlice = users.slice(); if (!usersSlice.isArray()) { LOG(ERR) << "cannot read users from _users collection"; return false; } clear(); if (usersSlice.length() == 0) { insertInitial(); } else { for (VPackSlice const& userSlice : VPackArrayIterator(usersSlice)) { AuthEntry auth = CreateAuthEntry(userSlice); if (auth.isActive()) { _authInfo[auth.username()] = auth; } }; } return true; } std::string AuthInfo::checkCache(std::string const& authorizationField, bool* mustChange) { READ_LOCKER(readLocker, _authInfoLock); auto const& it = _authCache.find(authorizationField); if (it != _authCache.end()) { AuthCache const& cached = it->second; #warning expires *mustChange = cached.mustChange(); return cached.username(); } // sorry, not found return ""; } bool AuthInfo::canUseDatabase(std::string const& username, char const* databaseName) { #warning TODO #if 0 READ_LOCKER(readLocker, _authInfoLock); AuthEntry const& entry = findUser(username); if (!entry.isActive()) { return false; } return entry._databases.find(databaseName) != entry.databases.end(); #endif return true; } AuthResult AuthInfo::checkAuthentication(std::string const& authorizationField, char const* databaseName) { return AuthResult(); } bool AuthInfo::populate(VPackSlice const& slice) { TRI_ASSERT(slice.isArray()); WRITE_LOCKER(writeLocker, _authInfoLock); clear(); for (VPackSlice const& authSlice : VPackArrayIterator(slice)) { AuthEntry auth = CreateAuthEntry(authSlice); if (auth.isActive()) { _authInfo.emplace(auth.username(), auth); } } return true; } void AuthInfo::insertInitial() { if (!_authInfo.empty()) { return; } try { VPackBuilder builder; builder.openArray(); // The only users object builder.add(VPackValue(VPackValueType::Object)); // username builder.add("user", VPackValue("root")); builder.add("authData", VPackValue(VPackValueType::Object)); // simple auth builder.add("simple", VPackValue(VPackValueType::Object)); builder.add("method", VPackValue("sha256")); char const* salt = "c776f5f4"; builder.add("salt", VPackValue(salt)); char const* hash = "ef74bc6fd59ac713bf5929c5ac2f42233e50d4d58748178132ea46dec433bd5b"; builder.add("hash", VPackValue(hash)); builder.close(); // simple builder.add("active", VPackValue(true)); builder.add("databases", VPackValue(VPackValueType::Array)); builder.add(VPackValue("*")); builder.close(); builder.close(); // authData builder.close(); // The user object builder.close(); // The Array populate(builder.slice()); } catch (...) { // No action } } #if 0 // no entry found in cache, decode the basic auth info and look it up std::string const up = StringUtils::decodeBase64(auth); std::string::size_type n = up.find(':', 0); if (n == std::string::npos || n == 0 || n + 1 > up.size()) { LOG(TRACE) << "invalid authentication data found, cannot extract " "username/password"; return GeneralResponse::ResponseCode::BAD; } username = up.substr(0, n); LOG(TRACE) << "checking authentication for user '" << username << "'"; //////////////////////////////////////////////////////////////////////////////// /// @brief check if a user can see a database /// note: "seeing" here does not necessarily mean the user can access the db. /// it only means there is a user account (with whatever password) present /// in the database //////////////////////////////////////////////////////////////////////////////// static bool CanUseDatabase(TRI_vocbase_t* vocbase, char const* username) { if (!vocbase->_settings.requireAuthentication) { // authentication is turned off return true; } if (strlen(username) == 0) { // will happen if username is "" (when converting it from a null value) // this will happen if authentication is turned off return true; } return TRI_ExistsAuthenticationAuthInfo(vocbase, username); } #endif