diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index 74652c7cc4..d26a33d059 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -1483,4 +1483,19 @@ Java_com_mapswithme_maps_Framework_nativeGetSearchBanners(JNIEnv * env, jclass) { return usermark_helper::ToBannersArray(env, frm()->GetAdsEngine().GetSearchBanners()); } + +JNIEXPORT void JNICALL +Java_com_mapswithme_maps_Framework_nativeAuthenticateUser(JNIEnv * env, jclass, + jstring socialToken, + jint socialTokenType) +{ + auto const tokenStr = jni::ToNativeString(env, socialToken); + frm()->GetUser().Authenticate(tokenStr, static_cast(socialTokenType)); +} + +JNIEXPORT jboolean JNICALL +Java_com_mapswithme_maps_Framework_nativeIsUserAuthenticated() +{ + return frm()->GetUser().IsAuthenticated(); +} } // extern "C" diff --git a/android/src/com/mapswithme/maps/Framework.java b/android/src/com/mapswithme/maps/Framework.java index b9856cf1d0..78e2b7ab25 100644 --- a/android/src/com/mapswithme/maps/Framework.java +++ b/android/src/com/mapswithme/maps/Framework.java @@ -77,6 +77,13 @@ public class Framework public static final int ROUTE_REBUILD_AFTER_POINTS_LOADING = 0; + @Retention(RetentionPolicy.SOURCE) + @IntDef({ SOCIAL_TOKEN_FACEBOOK, SOCIAL_TOKEN_GOOGLE }) + public @interface SocialTokenType {} + + public static final int SOCIAL_TOKEN_FACEBOOK = 0; + public static final int SOCIAL_TOKEN_GOOGLE = 1; + @SuppressWarnings("unused") public interface MapObjectListener { @@ -370,4 +377,8 @@ public class Framework public static native void nativeDeleteSavedRoutePoints(); public static native Banner[] nativeGetSearchBanners(); + + public static native void nativeAuthenticateUser(@NonNull String socialToken, + @SocialTokenType int socialTokenType); + public static native boolean nativeIsUserAuthenticated(); } diff --git a/configure.sh b/configure.sh index 5b1f763bd0..b8750ec600 100755 --- a/configure.sh +++ b/configure.sh @@ -87,6 +87,7 @@ else #define LOCALS_API_KEY "" #define LOCALS_API_URL "" #define LOCALS_PAGE_URL "" +#define PASSPORT_URL "" ' > "$PRIVATE_HEADER" echo 'ext { diff --git a/map/CMakeLists.txt b/map/CMakeLists.txt index 887258c910..5afc32ca30 100644 --- a/map/CMakeLists.txt +++ b/map/CMakeLists.txt @@ -68,6 +68,8 @@ set( track.hpp traffic_manager.cpp traffic_manager.hpp + user.cpp + user.hpp user_mark_container.cpp user_mark_container.hpp user_mark.cpp diff --git a/map/framework.hpp b/map/framework.hpp index 592876897e..584ec28999 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -12,6 +12,7 @@ #include "map/routing_mark.hpp" #include "map/track.hpp" #include "map/traffic_manager.hpp" +#include "map/user.hpp" #include "drape_frontend/gui/skin.hpp" #include "drape_frontend/drape_api.hpp" @@ -203,6 +204,8 @@ protected: LocalAdsManager m_localAdsManager; + User m_user; + /// This function will be called by m_storage when latest local files /// is downloaded. void OnCountryFileDownloaded(storage::TCountryId const & countryId, storage::TLocalFilePtr const localFile); @@ -236,6 +239,9 @@ public: df::DrapeApi & GetDrapeApi() { return m_drapeApi; } + User & GetUser() { return m_user; } + User const & GetUser() const { return m_user; } + /// Migrate to new version of very different data. bool IsEnoughSpaceForMigrate() const; storage::TCountryId PreMigrate(ms::LatLon const & position, storage::Storage::TChangeCountryFunction const & change, diff --git a/map/map.pro b/map/map.pro index 4e709df81f..f4f6548776 100644 --- a/map/map.pro +++ b/map/map.pro @@ -38,6 +38,7 @@ HEADERS += \ taxi_delegate.hpp \ track.hpp \ traffic_manager.hpp \ + user.hpp \ user_mark.hpp \ user_mark_container.hpp \ @@ -72,6 +73,7 @@ SOURCES += \ taxi_delegate.cpp \ track.cpp \ traffic_manager.cpp \ + user.cpp \ user_mark.cpp \ user_mark_container.cpp \ diff --git a/map/user.cpp b/map/user.cpp new file mode 100644 index 0000000000..0b8991b2ae --- /dev/null +++ b/map/user.cpp @@ -0,0 +1,165 @@ +#include "map/user.hpp" + +#include "platform/http_client.hpp" +#include "platform/platform.hpp" + +#include "coding/url_encode.hpp" + +#include "base/logging.hpp" + +#include "3party/jansson/myjansson.hpp" + +#include +#include +#include + +#define STAGE_PASSPORT_SERVER +#include "private.h" + +namespace +{ +std::string const kMapsMeTokenKey = "MapsMeToken"; +std::string const kServerUrl = PASSPORT_URL; + +std::string AuthenticationUrl(std::string const & socialToken, + User::SocialTokenType socialTokenType) +{ + if (kServerUrl.empty()) + return {}; + + std::string socialTokenStr; + switch (socialTokenType) + { + case User::SocialTokenType::Facebook: + socialTokenStr = "facebook"; + break; + case User::SocialTokenType::Google: + socialTokenStr = "google"; + break; + default: + LOG(LWARNING, ("Unknown social token type")); + return {}; + } + + std::ostringstream ss; + ss << kServerUrl << "/register-by-token/" << socialTokenStr + << "/?access_token=" << UrlEncode(socialToken); + return ss.str(); +} + +std::string ParseAccessToken(std::string const & src) +{ + my::Json root(src.c_str()); + std::string tokenStr; + FromJSONObject(root.get(), "access_token", tokenStr); + return tokenStr; +} +} // namespace + +User::User() +{ + Init(); +} + +User::~User() +{ + std::lock_guard lock(m_mutex); + m_needTerminate = true; + m_condition.notify_one(); +} + +void User::Init() +{ + std::string token; + if (GetPlatform().GetSecureStorage().Load(kMapsMeTokenKey, token)) + { + std::lock_guard lock(m_mutex); + m_accessToken = token; + } +} + +void User::ResetAccessToken() +{ + std::lock_guard lock(m_mutex); + m_accessToken.clear(); + GetPlatform().GetSecureStorage().Remove(kMapsMeTokenKey); +} + +bool User::IsAuthenticated() const +{ + std::lock_guard lock(m_mutex); + return !m_accessToken.empty(); +} + +std::string const & User::GetAccessToken() const +{ + std::lock_guard lock(m_mutex); + return m_accessToken; +} + +void User::SetAccessToken(std::string const & accessToken) +{ + std::lock_guard lock(m_mutex); + m_accessToken = accessToken; + GetPlatform().GetSecureStorage().Save(kMapsMeTokenKey, m_accessToken); +} + +void User::Authenticate(std::string const & socialToken, SocialTokenType socialTokenType) +{ + std::string const url = AuthenticationUrl(socialToken, socialTokenType); + if (url.empty()) + { + LOG(LWARNING, ("Passport service is unavailable.")); + return; + } + + { + std::lock_guard lock(m_mutex); + if (m_authenticationInProgress) + return; + m_authenticationInProgress = true; + } + + //TODO: refactor this after adding support of delayed tasks in WorkerThread. + m_workerThread.Push([this, url]() + { + uint8_t constexpr kAttemptsCount = 3; + uint32_t constexpr kWaitingInSeconds = 5; + uint32_t constexpr kDegradationScalar = 2; + + uint32_t waitingTime = kWaitingInSeconds; + for (uint8_t i = 0; i < kAttemptsCount; ++i) + { + platform::HttpClient request(url); + request.SetRawHeader("Accept", "application/json"); + // TODO: Now passport service uses redirection. If it becomes false, uncomment checking. + if (request.RunHttpRequest())// && !request.WasRedirected()) + { + if (request.ErrorCode() == 200) // Ok. + { + SetAccessToken(ParseAccessToken(request.ServerResponse())); + break; + } + + if (request.ErrorCode() == 403) // Forbidden. + { + ResetAccessToken(); + break; + } + } + + // Wait for some time and retry. + std::unique_lock lock(m_mutex); + m_condition.wait_for(lock, std::chrono::seconds(waitingTime), + [this]{return m_needTerminate;}); + if (m_needTerminate) + break; + waitingTime *= kDegradationScalar; + } + + { + std::lock_guard lock(m_mutex); + m_authenticationInProgress = false; + } + }); +} diff --git a/map/user.hpp b/map/user.hpp new file mode 100644 index 0000000000..da8b95043e --- /dev/null +++ b/map/user.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "base/worker_thread.hpp" + +#include +#include +#include + +// This class is thread-safe. +class User +{ +public: + enum SocialTokenType + { + Facebook, + Google + }; + + User(); + ~User(); + void Authenticate(std::string const & socialToken, SocialTokenType socialTokenType); + bool IsAuthenticated() const; + void ResetAccessToken(); + + std::string const & GetAccessToken() const; + +private: + void Init(); + void SetAccessToken(std::string const & accessToken); + + std::string m_accessToken; + mutable std::mutex m_mutex; + std::condition_variable m_condition; + bool m_needTerminate = false; + bool m_authenticationInProgress = false; + base::WorkerThread m_workerThread; +}; diff --git a/xcode/map/map.xcodeproj/project.pbxproj b/xcode/map/map.xcodeproj/project.pbxproj index 2b8a35793e..7b9ad21899 100644 --- a/xcode/map/map.xcodeproj/project.pbxproj +++ b/xcode/map/map.xcodeproj/project.pbxproj @@ -32,6 +32,8 @@ 454649F21F2728CE00EF4064 /* local_ads_mark.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 454649F01F2728CE00EF4064 /* local_ads_mark.hpp */; }; 45580ABE1E2CBD5E00CD535D /* benchmark_tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45580ABC1E2CBD5E00CD535D /* benchmark_tools.cpp */; }; 45580ABF1E2CBD5E00CD535D /* benchmark_tools.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 45580ABD1E2CBD5E00CD535D /* benchmark_tools.hpp */; }; + 45A2D9D51F7556EB003310A0 /* user.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45A2D9D31F7556EB003310A0 /* user.cpp */; }; + 45A2D9D61F7556EB003310A0 /* user.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 45A2D9D41F7556EB003310A0 /* user.hpp */; }; 45D287671E966E3400587F05 /* liblocal_ads.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45D287661E966E3400587F05 /* liblocal_ads.a */; }; 670E39401C46C5C700E9C0A6 /* gps_tracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670E393E1C46C5C700E9C0A6 /* gps_tracker.cpp */; }; 670E39411C46C5C700E9C0A6 /* gps_tracker.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 670E393F1C46C5C700E9C0A6 /* gps_tracker.hpp */; }; @@ -162,6 +164,8 @@ 454649F01F2728CE00EF4064 /* local_ads_mark.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = local_ads_mark.hpp; sourceTree = ""; }; 45580ABC1E2CBD5E00CD535D /* benchmark_tools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = benchmark_tools.cpp; sourceTree = ""; }; 45580ABD1E2CBD5E00CD535D /* benchmark_tools.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = benchmark_tools.hpp; sourceTree = ""; }; + 45A2D9D31F7556EB003310A0 /* user.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user.cpp; sourceTree = ""; }; + 45A2D9D41F7556EB003310A0 /* user.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = user.hpp; sourceTree = ""; }; 45D287661E966E3400587F05 /* liblocal_ads.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblocal_ads.a; path = "../../../omim-build/xcode/Debug-iphonesimulator/liblocal_ads.a"; sourceTree = ""; }; 670E393E1C46C5C700E9C0A6 /* gps_tracker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gps_tracker.cpp; sourceTree = ""; }; 670E393F1C46C5C700E9C0A6 /* gps_tracker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = gps_tracker.hpp; sourceTree = ""; }; @@ -435,6 +439,8 @@ 675345BD1A4054AD00A0A8C3 /* map */ = { isa = PBXGroup; children = ( + 45A2D9D31F7556EB003310A0 /* user.cpp */, + 45A2D9D41F7556EB003310A0 /* user.hpp */, 675345CB1A4054E800A0A8C3 /* address_finder.cpp */, 45201E921CE4AC90008A4842 /* api_mark_point.cpp */, 34921F611BFA0A6900737D6E /* api_mark_point.hpp */, @@ -523,6 +529,7 @@ 3D47B2941F054BC5000828D2 /* taxi_delegate.hpp in Headers */, 3D47B2C81F20EF06000828D2 /* displayed_categories_modifiers.hpp in Headers */, 348AB57D1D7EE0C6009F8301 /* chart_generator.hpp in Headers */, + 45A2D9D61F7556EB003310A0 /* user.hpp in Headers */, F63421F91DF9BF9100A96868 /* reachable_by_taxi_checker.hpp in Headers */, 6753469E1A4054E800A0A8C3 /* user_mark_container.hpp in Headers */, 675346491A4054E800A0A8C3 /* bookmark_manager.hpp in Headers */, @@ -663,6 +670,7 @@ 675346661A4054E800A0A8C3 /* ge0_parser.cpp in Sources */, F6D2CE7E1EDEB7F500636DFD /* routing_manager.cpp in Sources */, 3D74ABBE1EA76F1D0063A898 /* local_ads_supported_types.cpp in Sources */, + 45A2D9D51F7556EB003310A0 /* user.cpp in Sources */, 0C2B73DE1E92AB9900530BB8 /* local_ads_manager.cpp in Sources */, F6B283071C1B03320081957A /* gps_track_storage.cpp in Sources */, 6753463A1A4054E800A0A8C3 /* address_finder.cpp in Sources */,