authdb: add getUserWithPhoneFromBackend api

parent d6ac16c2
......@@ -30,10 +30,23 @@ FileAuthDb::FileAuthDb() {
GenericStruct *cr = GenericManager::get()->getRoot();
GenericStruct *ma = cr->get<GenericStruct>("module::Authentication");
mLastSync = 0;
mFileString = ma->get<ConfigString>("datasource")->read();
sync();
}
void FileAuthDb::getUserWithPhoneFromBackend(const char* phone, const char* domain, AuthDbListener *listener) {
AuthDbResult res = AuthDbResult::PASSWORD_NOT_FOUND;
if (mLastSync == 0) {
sync();
}
std::string user;
if (getCachedUserWithPhone(phone, domain, user) == VALID_PASS_FOUND) {
res = AuthDbResult::PASSWORD_FOUND;
}
if (listener) listener->onResult(res, user);
}
void FileAuthDb::getPasswordFromBackend(const std::string &id, const std::string &domain,
const std::string &authid, AuthDbListener *listener) {
AuthDbResult res = AuthDbResult::PASSWORD_NOT_FOUND;
......@@ -43,7 +56,7 @@ void FileAuthDb::getPasswordFromBackend(const std::string &id, const std::string
sync();
}
string key(createPasswordKey(id, domain, authid));
string key(createPasswordKey(id, authid));
std::string passwd;
if (getCachedPassword(key, domain, passwd) == VALID_PASS_FOUND) {
......@@ -70,6 +83,7 @@ void FileAuthDb::sync() {
string domain;
string password;
string userid;
string phone;
LOGD("Opening file %s", mFileString.c_str());
file.open(mFileString);
......@@ -81,21 +95,29 @@ void FileAuthDb::sync() {
domain.clear();
password.clear();
userid.clear();
phone.clear();
try {
getline(ss, user, '@');
getline(ss, domain, ' ');
getline(ss, password, ' ');
if (!ss.eof()) {
getline(ss, userid);
getline(ss, userid, ' ');
if (!ss.eof()) {
getline(ss, phone);
} else {
phone = user;
}
} else {
userid = user;
userid = phone = user;
}
cacheUserWithPhone(phone, domain, user);
if (find(domains.begin(), domains.end(), domain) != domains.end()) {
string key(createPasswordKey(user, domain, userid));
string key(createPasswordKey(user, userid));
cachePassword(key, domain, password, mCacheExpire);
} else if (find(domains.begin(), domains.end(), "*") != domains.end()) {
string key(createPasswordKey(user, domain, userid));
string key(createPasswordKey(user, userid));
cachePassword(key, domain, password, mCacheExpire);
} else {
LOGW("Not handled domain: %s", domain.c_str());
......
......@@ -581,8 +581,13 @@ AuthDbResult OdbcAuthDb::doRetrievePassword(ConnectionCtx &ctx, const string &id
timings.tGotResult = steady_clock::now();
foundPassword.assign((char *)password);
string key(createPasswordKey(id, domain, auth));
string key(createPasswordKey(id, auth));
cachePassword(key, domain, foundPassword, -1);
LOGD("Password found %s for %s", foundPassword.c_str(), id.c_str());
return PASSWORD_FOUND;
}
void OdbcAuthDb::getUserWithPhoneFromBackend(const char* phone, const char* domain, AuthDbListener *listener) {
LOGE("%s not supported with ODBC", __FUNCTION__);
if (listener) listener->onResult(AuthDbResult::PASSWORD_NOT_FOUND, "");
}
......@@ -84,6 +84,7 @@ SociAuthDB::SociAuthDB() : conn_pool(NULL) {
connection_string = ma->get<ConfigString>("soci-connection-string")->read();
backend = ma->get<ConfigString>("soci-backend")->read();
get_password_request = ma->get<ConfigString>("soci-password-request")->read();
get_user_with_phone_request = ma->get<ConfigString>("soci-user-with-phone-request")->read();
unsigned int max_queue_size = (unsigned int)ma->get<ConfigInt>("soci-max-queue-size")->read();
conn_pool = new connection_pool(poolSize);
......@@ -118,7 +119,7 @@ void SociAuthDB::getPasswordWithPool(const std::string &id, const std::string &d
steady_clock::time_point stop;
std::string pass;
session *sql = NULL;
try {
start = steady_clock::now();
// will grab a connection from the pool. This is thread safe
......@@ -132,7 +133,7 @@ void SociAuthDB::getPasswordWithPool(const std::string &id, const std::string &d
*sql << get_password_request, into(pass), use(id, "id"), use(domain, "domain"), use(authid, "authid");
stop = steady_clock::now();
SLOGD << "[SOCI] Got pass for " << id << " in " << DURATION_MS(start, stop) << "ms";
cachePassword(createPasswordKey(id, domain, authid), domain, pass, mCacheExpire);
cachePassword(createPasswordKey(id, authid), domain, pass, mCacheExpire);
if (listener){
listener->onResult(pass.empty() ? PASSWORD_NOT_FOUND : PASSWORD_FOUND, pass);
}
......@@ -155,6 +156,50 @@ void SociAuthDB::getPasswordWithPool(const std::string &id, const std::string &d
if (sql) delete sql;
}
void SociAuthDB::getUserWithPhoneWithPool(const std::string &phone, const std::string &domain, AuthDbListener *listener) {
steady_clock::time_point start;
steady_clock::time_point stop;
std::string user;
session *sql = NULL;
try {
start = steady_clock::now();
// will grab a connection from the pool. This is thread safe
sql = new session(*conn_pool); //this may raise a soci_error exception, so keep it in the try block.
stop = steady_clock::now();
SLOGD << "[SOCI] Pool acquired in " << DURATION_MS(start, stop) << "ms";
start = stop;
*sql << get_user_with_phone_request, into(user), use(phone, "phone");
stop = steady_clock::now();
if (!user.empty()) {
SLOGD << "[SOCI] Got user for " << phone << " in " << DURATION_MS(start, stop) << "ms";
cacheUserWithPhone(phone, domain, user);
}
if (listener){
listener->onResult(user.empty() ? PASSWORD_NOT_FOUND : PASSWORD_FOUND, user);
}
} catch (mysql_soci_error const &e) {
stop = steady_clock::now();
SLOGE << "[SOCI] MySQL error after " << DURATION_MS(start, stop) << "ms : " << e.err_num_ << " " << e.what();
if (listener) listener->onResult(PASSWORD_NOT_FOUND, user);
if (sql) reconnectSession(*sql);
} catch (exception const &e) {
stop = steady_clock::now();
SLOGE << "[SOCI] Some other error after " << DURATION_MS(start, stop) << "ms : " << e.what();
if (listener) listener->onResult(PASSWORD_NOT_FOUND, user);
if (sql) reconnectSession(*sql);
}
if (sql) delete sql;
}
#pragma mark - Inherited virtuals
void SociAuthDB::getPasswordFromBackend(const std::string &id, const std::string &domain,
......@@ -170,6 +215,17 @@ void SociAuthDB::getPasswordFromBackend(const std::string &id, const std::string
<< authid;
if (listener) listener->onResult(AUTH_ERROR, "");
}
}
void SociAuthDB::getUserWithPhoneFromBackend(const char *phone, const char* domain, AuthDbListener *listener) {
// create a thread to grab a pool connection and use it to retrieve the auth information
auto func = bind(&SociAuthDB::getUserWithPhoneWithPool, this, std::string(phone), std::string(domain), listener);
return;
bool success = thread_pool->Enqueue(func);
if (success == FALSE) {
// Enqueue() can fail when the queue is full, so we have to act on that
SLOGE << "[SOCI] Auth queue is full, cannot fullfil user request for " << phone;
if (listener) listener->onResult(AUTH_ERROR, "");
}
}
......@@ -31,6 +31,9 @@ class FixedAuthDb : public AuthDbBackend {
FixedAuthDb() {
}
virtual void getUserWithPhoneFromBackend(const char* phone, const char *domain, AuthDbListener *listener) {
if (listener) listener->onResult(PASSWORD_FOUND, "user@domain.com");
}
virtual void getPasswordFromBackend(const std::string &id, const std::string &domain,
const std::string &authid, AuthDbListener *listener) {
if (listener) listener->onResult(PASSWORD_FOUND, "fixed");
......@@ -82,7 +85,7 @@ void AuthDbBackend::declareConfig(GenericStruct *mc) {
#endif
}
string AuthDbBackend::createPasswordKey(const string &user, const string &host, const string &auth_username) {
string AuthDbBackend::createPasswordKey(const string &user, const string &auth_username) {
ostringstream key;
key << user << "#" << auth_username;
return key.str();
......@@ -125,13 +128,20 @@ bool AuthDbBackend::cachePassword(const string &key, const string &domain, const
return true;
}
bool AuthDbBackend::cacheUserWithPhone(const std::string &phone, const std::string &domain, const std::string &user) {
unique_lock<mutex> lck(mCachedUserWithPhoneMutex);
mPhone2User[phone + "@" + domain] = user;
mPhone2User[user + "@" + domain] = user;
return true;
}
void AuthDbBackend::getPassword(const char* user, const char* host, const char *auth_username,
AuthDbListener *listener) {
// Check for usable cached password
string id(user);
string domain(host);
string auth(auth_username);
string key(createPasswordKey(id, domain, auth));
string key(createPasswordKey(id, auth));
string pass;
switch (getCachedPassword(key, domain, pass)) {
case VALID_PASS_FOUND:
......@@ -153,11 +163,39 @@ void AuthDbBackend::getPassword(const char* user, const char* host, const char *
void AuthDbBackend::createCachedAccount(const char* user, const char* host, const char *auth_username, const char *password,
int expires) {
if (user && host) {
string key = createPasswordKey(user, host, auth_username ? auth_username : "");
string key = createPasswordKey(user, auth_username ? auth_username : "");
cachePassword(key, host, password, expires);
cacheUserWithPhone(user, host, user);
}
}
void AuthDbBackend::createAccount(const char* user, const char* host, const char *auth_username, const char *password, int expires) {
createCachedAccount(user, host, auth_username, password, expires);
}
AuthDbBackend::CacheResult AuthDbBackend::getCachedUserWithPhone(const string &phone, const string &domain, string &user) {
time_t now = getCurrentTime();
unique_lock<mutex> lck(mCachedUserWithPhoneMutex);
auto it = mPhone2User.find(phone + "@" + domain);
if (it != mPhone2User.end()) {
user.assign(it->second);
return VALID_PASS_FOUND;
}
return NO_PASS_FOUND;
}
void AuthDbBackend::getUserWithPhone(const char* phone, const char* domain, AuthDbListener *listener) {
// Check for usable cached password
string user;
switch (getCachedUserWithPhone(std::string(phone), std::string(domain), user)) {
case VALID_PASS_FOUND:
if (listener) listener->onResult(AuthDbResult::PASSWORD_FOUND, user);
return;
case EXPIRED_PASS_FOUND:
case NO_PASS_FOUND:
break;
}
// if we reach here, password wasn't cached: we have to grab the password from the actual backend
getUserWithPhoneFromBackend(phone, domain, listener);
}
......@@ -61,24 +61,31 @@ class AuthDbBackend {
}
};
private:
std::map<std::string, std::map<std::string, CachedPassword>> mCachedPasswords;
std::mutex mCachedPasswordMutex;
std::mutex mCachedUserWithPhoneMutex;
map<string, string> mPhone2User;
protected:
AuthDbBackend();
enum CacheResult { VALID_PASS_FOUND, EXPIRED_PASS_FOUND, NO_PASS_FOUND };
std::string createPasswordKey(const std::string &user, const std::string &host, const std::string &auth);
std::string createPasswordKey(const std::string &user, const std::string &auth);
bool cachePassword(const std::string &key, const std::string &domain, const std::string &pass, int expires);
bool cacheUserWithPhone(const std::string &phone, const std::string &domain, const std::string &user);
CacheResult getCachedPassword(const std::string &key, const std::string &domain, std::string &pass);
void createCachedAccount(const char* user, const char* host, const char *auth_username, const char *password, int expires);
CacheResult getCachedUserWithPhone(const string &phone, const string &domain, string &user);
void createCachedAccount(const char* user, const char* domain, const char *auth_username, const char *password, int expires);
void clearCache();
int mCacheExpire;
public:
virtual ~AuthDbBackend();
// warning: listener may be invoked on authdb backend thread, so listener must be threadsafe somehow!
void getPassword(const char* user, const char* host, const char *auth_username, AuthDbListener *listener);
void getPassword(const char* user, const char* domain, const char *auth_username, AuthDbListener *listener);
void getUserWithPhone(const char* phone, const char* domain, AuthDbListener *listener);
virtual void getUserWithPhoneFromBackend(const char* phone, const char* domain, AuthDbListener *listener) = 0;
virtual void createAccount(const char* user, const char* host, const char *auth_username, const char *password, int expires);
virtual void createAccount(const char* user, const char* domain, const char *auth_username, const char *password, int expires);
virtual void getPasswordFromBackend(const std::string &id, const std::string &domain,
const std::string &authid, AuthDbListener *listener) = 0;
......@@ -101,6 +108,7 @@ class FileAuthDb : public AuthDbBackend {
public:
FileAuthDb();
virtual void getUserWithPhoneFromBackend(const char* phone, const char* domain, AuthDbListener *listener);
virtual void getPasswordFromBackend(const std::string &id, const std::string &domain,
const std::string &authid, AuthDbListener *listener);
......@@ -142,12 +150,13 @@ class OdbcAuthDb : public AuthDbBackend {
void envError(const char *doing);
bool execDirect;
bool getConnection(const std::string &id, ConnectionCtx &ctx, AuthDbTimings &timings);
AuthDbResult doRetrievePassword(ConnectionCtx &ctx, const std::string &user, const std::string &host,
AuthDbResult doRetrievePassword(ConnectionCtx &ctx, const std::string &user, const std::string &domain,
const std::string &auth, std::string &foundPassword, AuthDbTimings &timings);
void doAsyncRetrievePassword(std::string id, std::string domain, std::string auth,
AuthDbListener *listener);
public:
virtual void getUserWithPhoneFromBackend(const char* phone, const char* domain, AuthDbListener *listener);
virtual void getPasswordFromBackend(const std::string &id, const std::string &domain,
const std::string &authid, AuthDbListener *listener);
std::map<std::string, std::string> cachedPasswords;
......@@ -171,12 +180,14 @@ class SociAuthDB : public AuthDbBackend {
public:
SociAuthDB();
void setConnectionParameters(const string &domain, const string &request);
virtual void getUserWithPhoneFromBackend(const char* phone, const char* domain, AuthDbListener *listener);
virtual void getPasswordFromBackend(const std::string &id, const std::string &domain,
const std::string &authid, AuthDbListener *listener);
static void declareConfig(GenericStruct *mc);
private:
void getUserWithPhoneWithPool(const std::string &phone, const std::string &domain, AuthDbListener *listener);
void getPasswordWithPool(const std::string &id, const std::string &domain,
const std::string &authid, AuthDbListener *listener);
......@@ -188,6 +199,7 @@ class SociAuthDB : public AuthDbBackend {
std::string connection_string;
std::string backend;
std::string get_password_request;
std::string get_user_with_phone_request;
};
#endif /* ENABLE_SOCI */
......
......@@ -21,12 +21,25 @@ public:
, "OnAuthListener to mainthread");
}
virtual void processResponse(AuthDbResult result, std::string passwd) {
virtual void processResponse(AuthDbResult result, std::string user) {
if (result == AuthDbResult::PASSWORD_FOUND) {
SLOGD << "Found user " << belle_sip_uri_get_user(mInfo->getEntity()) << ", adding presence information";
mInfo->setDefaultElement();
} else {
SLOGD << "Could not found user " << belle_sip_uri_get_user(mInfo->getEntity()) << ", ignoring";
// result is a phone alias if (and only if) user is not the same as the entity user
bool isPhone = (strcmp(user.c_str(), belle_sip_uri_get_user(mInfo->getEntity())) != 0);
if (isPhone) {
SLOGD << "Found user " << user << " for phone " << belle_sip_uri_get_user(mInfo->getEntity()) << ", adding presence information";
// change contact accordingly
char *contact_as_string = belle_sip_uri_to_string(mInfo->getEntity());
belle_sip_uri_t *uri = belle_sip_uri_parse(contact_as_string);
belle_sip_uri_set_user(uri, user.c_str());
belle_sip_free(contact_as_string);
contact_as_string = belle_sip_uri_to_string(uri);
belle_sip_object_unref(uri);
mInfo->setDefaultElement(contact_as_string);
belle_sip_free(contact_as_string);
} else {
SLOGD << "Found user " << user << ", adding presence information";
mInfo->setDefaultElement();
}
}
delete this;
}
......@@ -38,6 +51,5 @@ private:
void PresenceLongterm::onNewPresenceInfo(const std::shared_ptr<PresentityPresenceInformation>& info) const {
const belle_sip_uri_t* uri = info->getEntity();
SLOGD << "New presence info for " << belle_sip_uri_get_user(uri) << ", checking if this user is already registered";
AuthDbBackend::get()->getPassword(belle_sip_uri_get_user(uri), belle_sip_uri_get_host(uri), belle_sip_uri_get_user(uri), new OnAuthListener(mMainLoop, info));
AuthDbBackend::get()->getUserWithPhone(belle_sip_uri_get_user(info->getEntity()), belle_sip_uri_get_host(info->getEntity()), new OnAuthListener(mMainLoop, info));
}
......@@ -178,8 +178,15 @@ string PresentityPresenceInformation::refreshTuplesForEtag(const string &eTag, i
return setOrUpdate(NULL, NULL, &eTag, expires);
}
void PresentityPresenceInformation::setDefaultElement(void) {
void PresentityPresenceInformation::setDefaultElement(const char *contact) {
mDefaultInformationElement = make_shared<PresenceInformationElement>(getEntity());
if (contact) {
for (auto & tup : mDefaultInformationElement->getTuples()) {
tup->setContact(::pidf::Contact(contact));
}
}
notifyAll();
}
......@@ -191,7 +198,7 @@ void PresentityPresenceInformation::removeTuplesForEtag(const string &eTag) {
delete informationElement;
notifyAll(); // Removing an event state change global state, so it should be notified
} else
SLOGD << "No tulpes found for etag [" << eTag << "]";
SLOGD << "No tuples found for etag [" << eTag << "]";
}
FlexisipException &operator<<(FlexisipException &ex, const PresentityPresenceInformation &p) {
......
......@@ -95,7 +95,7 @@ class PresentityPresenceInformation : public std::enable_shared_from_this<Presen
* */
string putTuples(pidf::Presence::TupleSequence &tuples, pidf::Presence::AnySequence &extensions, int expires);
void setDefaultElement(void);
void setDefaultElement(const char* contact = NULL);
/*
*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment