forked from organicmaps/organicmaps
[oauth] Employ OSM OAuth for OSM API requests
Again, a major rework of OAuth API, and now ServerApi06 uses it. No more storing passwords, now it's request tokens. Also, this commit adds consumer tokens to configure.sh (and private.h), and updates iphone UI to use new auth process. And adds liboauthcpp library to both Android and iPhone projects.
This commit is contained in:
parent
cf2302d034
commit
5f32a262b1
12 changed files with 252 additions and 156 deletions
|
@ -25,7 +25,7 @@ define add_prebuild_static_lib
|
|||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
endef
|
||||
|
||||
prebuild_static_libs := osrm protobuf tomcrypt jansson minizip fribidi freetype expat succinct opening_hours pugixml \
|
||||
prebuild_static_libs := osrm protobuf tomcrypt jansson minizip fribidi freetype expat succinct opening_hours pugixml oauthcpp \
|
||||
base coding geometry editor platform indexer storage search routing drape drape_frontend map stats_client
|
||||
|
||||
$(foreach item,$(prebuild_static_libs),$(eval $(call add_prebuild_static_lib,$(item))))
|
||||
|
@ -42,7 +42,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../
|
|||
|
||||
LOCAL_MODULE := mapswithme
|
||||
LOCAL_STATIC_LIBRARIES := map drape_frontend routing search storage indexer drape platform editor geometry coding base \
|
||||
expat freetype fribidi minizip jansson tomcrypt protobuf osrm stats_client succinct opening_hours pugixml
|
||||
expat freetype fribidi minizip jansson tomcrypt protobuf osrm stats_client succinct opening_hours pugixml oauthcpp
|
||||
|
||||
LOCAL_CFLAGS := -ffunction-sections -fdata-sections -Wno-extern-c-compat
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ else
|
|||
#define MY_TARGET_KEY ""
|
||||
#define PARSE_APPLICATION_ID ""
|
||||
#define PARSE_CLIENT_KEY ""
|
||||
#define OSM_CONSUMER_KEY ""
|
||||
#define OSM_CONSUMER_SECRET ""
|
||||
#define MWM_GEOLOCATION_SERVER ""
|
||||
#define OSRM_ONLINE_SERVER_URL ""
|
||||
#define RESOURCES_METASERVER_URL ""
|
||||
|
|
|
@ -32,17 +32,18 @@ UNIT_TEST(OSM_Auth_Login)
|
|||
auto result = auth.AuthorizePassword(kTestUser, kTestPassword, token);
|
||||
TEST_EQUAL(result, OsmOAuth::AuthResult::OK, ("login to test server"));
|
||||
TEST(token.IsValid(), ("authorized"));
|
||||
string const perm = auth.Request(token, "/permissions");
|
||||
TEST(perm.find("write_api") != string::npos, ("can write to api"));
|
||||
OsmOAuth::Response const perm = auth.Request(token, "/permissions");
|
||||
TEST_EQUAL(perm.first, OsmOAuth::ResponseCode::OK, ("permission request ok"));
|
||||
TEST(perm.second.find("write_api") != string::npos, ("can write to api"));
|
||||
}
|
||||
|
||||
UNIT_TEST(OSM_Auth_Facebook)
|
||||
{
|
||||
OsmOAuth auth(kConsumerKey, kConsumerSecret, kTestServer, kTestServer);
|
||||
ClientToken token;
|
||||
auto result = auth.AuthorizeFacebook(kFacebookToken, token);
|
||||
auto result = auth.AuthorizeFacebook(kFacebookToken);
|
||||
TEST_EQUAL(result, OsmOAuth::AuthResult::OK, ("login via facebook"));
|
||||
TEST(token.IsValid(), ("authorized"));
|
||||
string const perm = auth.Request(token, "/permissions");
|
||||
TEST(perm.find("write_api") != string::npos, ("can write to api"));
|
||||
TEST(auth.IsAuthorized(), ("authorized"));
|
||||
OsmOAuth::Response const perm = auth.Request("/permissions");
|
||||
TEST_EQUAL(perm.first, OsmOAuth::ResponseCode::OK, ("permission with stored token request ok"));
|
||||
TEST(perm.second.find("write_api") != string::npos, ("can write to api"));
|
||||
}
|
||||
|
|
|
@ -7,29 +7,36 @@
|
|||
#include "3party/pugixml/src/pugixml.hpp"
|
||||
|
||||
using osm::ServerApi06;
|
||||
using osm::OsmOAuth;
|
||||
using namespace pugi;
|
||||
|
||||
constexpr char const * kOsmDevServer = "http://master.apis.dev.openstreetmap.org";
|
||||
constexpr char const * kOsmConsumerKey = "eRtN6yKZZf34oVyBnyaVbsWtHIIeptLArQKdTwN3";
|
||||
constexpr char const * kOsmConsumerSecret = "lC124mtm2VqvKJjSh35qBpKfrkeIjpKuGe38Hd1H";
|
||||
constexpr char const * kValidOsmUser = "MapsMeTestUser";
|
||||
constexpr char const * kInvalidOsmUser = "qwesdxzcgretwr";
|
||||
ServerApi06 const kApi(kValidOsmUser, "12345678", kOsmDevServer);
|
||||
constexpr char const * kValidOsmPassword = "12345678";
|
||||
|
||||
UNIT_TEST(OSM_ServerAPI_CheckUserAndPassword)
|
||||
UNIT_TEST(OSM_ServerAPI_TestUserExists)
|
||||
{
|
||||
TEST(kApi.CheckUserAndPassword(), ());
|
||||
|
||||
my::LogLevelSuppressor s;
|
||||
TEST(!ServerApi06(kInvalidOsmUser, "3345dfce2", kOsmDevServer).CheckUserAndPassword(), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(OSM_ServerAPI_HttpCodeForUrl)
|
||||
{
|
||||
TEST_EQUAL(200, ServerApi06::HttpCodeForUrl(string(kOsmDevServer) + "/user/" + kValidOsmUser), ());
|
||||
TEST_EQUAL(404, ServerApi06::HttpCodeForUrl(string(kOsmDevServer) + "/user/" + kInvalidOsmUser), ());
|
||||
OsmOAuth auth(kOsmConsumerKey, kOsmConsumerSecret, kOsmDevServer);
|
||||
ServerApi06 api(auth);
|
||||
TEST(api.TestUserExists(kValidOsmUser), ());
|
||||
TEST(!api.TestUserExists(kInvalidOsmUser), ());
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
ServerApi06 CreateAPI()
|
||||
{
|
||||
OsmOAuth auth(kOsmConsumerKey, kOsmConsumerSecret, kOsmDevServer);
|
||||
OsmOAuth::AuthResult result = auth.AuthorizePassword(kValidOsmUser, kValidOsmPassword);
|
||||
TEST_EQUAL(result, OsmOAuth::AuthResult::OK, ());
|
||||
TEST(auth.IsAuthorized(), ("OSM authorization"));
|
||||
ServerApi06 api(auth);
|
||||
return api;
|
||||
}
|
||||
|
||||
// id attribute is set to -1.
|
||||
// version attribute is set to 1.
|
||||
void GenerateNodeXml(double lat, double lon, ServerApi06::TKeyValueTags const & tags, xml_document & outNode)
|
||||
|
@ -88,36 +95,38 @@ UNIT_TEST(SetAttributeForOsmNode)
|
|||
|
||||
UNIT_TEST(OSM_ServerAPI_ChangesetActions)
|
||||
{
|
||||
ServerApi06 api = CreateAPI();
|
||||
|
||||
uint64_t changeSetId;
|
||||
TEST(kApi.CreateChangeSet({{"created_by", "MAPS.ME Unit Test"}, {"comment", "For test purposes only."}}, changeSetId), ());
|
||||
TEST(api.CreateChangeSet({{"created_by", "MAPS.ME Unit Test"}, {"comment", "For test purposes only."}}, changeSetId), ());
|
||||
|
||||
xml_document node;
|
||||
GenerateNodeXml(11.11, 12.12, {{"testkey", "firstnode"}}, node);
|
||||
TEST(SetAttributeForOsmNode(node, "changeset", changeSetId), ());
|
||||
|
||||
uint64_t nodeId;
|
||||
TEST(kApi.CreateNode(XmlToString(node), nodeId), ());
|
||||
TEST(api.CreateNode(XmlToString(node), nodeId), ());
|
||||
TEST(SetAttributeForOsmNode(node, "id", nodeId), ());
|
||||
|
||||
TEST(SetAttributeForOsmNode(node, "lat", 10.10), ());
|
||||
TEST(kApi.ModifyNode(XmlToString(node), nodeId), ());
|
||||
TEST(api.ModifyNode(XmlToString(node), nodeId), ());
|
||||
// After modification, node version has increased.
|
||||
TEST(SetAttributeForOsmNode(node, "version", 2), ());
|
||||
|
||||
// To retrieve created node, changeset should be closed first.
|
||||
TEST(kApi.CloseChangeSet(changeSetId), ());
|
||||
TEST(api.CloseChangeSet(changeSetId), ());
|
||||
|
||||
TEST(kApi.CreateChangeSet({{"created_by", "MAPS.ME Unit Test"}, {"comment", "For test purposes only."}}, changeSetId), ());
|
||||
TEST(api.CreateChangeSet({{"created_by", "MAPS.ME Unit Test"}, {"comment", "For test purposes only."}}, changeSetId), ());
|
||||
// New changeset has new id.
|
||||
TEST(SetAttributeForOsmNode(node, "changeset", changeSetId), ());
|
||||
|
||||
string const serverReply = kApi.GetXmlNodeByLatLon(node.child("osm").child("node").attribute("lat").as_double(),
|
||||
string const serverReply = api.GetXmlNodeByLatLon(node.child("osm").child("node").attribute("lat").as_double(),
|
||||
node.child("osm").child("node").attribute("lon").as_double());
|
||||
xml_document reply;
|
||||
reply.load_string(serverReply.c_str());
|
||||
TEST_EQUAL(nodeId, reply.child("osm").child("node").attribute("id").as_ullong(), ());
|
||||
|
||||
TEST(ServerApi06::DeleteResult::ESuccessfullyDeleted == kApi.DeleteNode(XmlToString(node), nodeId), ());
|
||||
TEST(ServerApi06::DeleteResult::ESuccessfullyDeleted == api.DeleteNode(XmlToString(node), nodeId), ());
|
||||
|
||||
TEST(kApi.CloseChangeSet(changeSetId), ());
|
||||
TEST(api.CloseChangeSet(changeSetId), ());
|
||||
}
|
||||
|
|
|
@ -49,6 +49,17 @@ bool isLoggedIn(string const & contents)
|
|||
}
|
||||
} // namespace
|
||||
|
||||
OsmOAuth::OsmOAuth(string const & consumerKey, string const & consumerSecret,
|
||||
string const & baseUrl, string const & apiUrl):
|
||||
m_consumerKey(consumerKey), m_consumerSecret(consumerSecret), m_baseUrl(baseUrl)
|
||||
{
|
||||
// If base URL has been changed, but api URL is omitted, we use the same base URL for api calls.
|
||||
if (apiUrl.empty() || (apiUrl == kDefaultApiURL && baseUrl != kDefaultBaseURL))
|
||||
m_apiUrl = baseUrl;
|
||||
else
|
||||
m_apiUrl = apiUrl;
|
||||
}
|
||||
|
||||
// Opens a login page and extract a cookie and a secret token.
|
||||
OsmOAuth::AuthResult OsmOAuth::FetchSessionId(OsmOAuth::SessionID & sid) const
|
||||
{
|
||||
|
@ -120,6 +131,8 @@ string OsmOAuth::SendAuthRequest(string const & requestTokenKey, SessionID const
|
|||
params["authenticity_token"] = sid.m_token;
|
||||
params["allow_read_prefs"] = "yes";
|
||||
params["allow_write_api"] = "yes";
|
||||
params["allow_write_gpx"] = "yes";
|
||||
params["allow_write_notes"] = "yes";
|
||||
params["commit"] = "Save changes";
|
||||
HTTPClientPlatformWrapper request(m_baseUrl + "/oauth/authorize");
|
||||
request.set_body_data(buildPostRequest(params), "application/x-www-form-urlencoded");
|
||||
|
@ -202,8 +215,9 @@ OsmOAuth::AuthResult OsmOAuth::AuthorizeFacebook(string const & facebookToken, C
|
|||
return FetchAccessToken(sid, token);
|
||||
}
|
||||
|
||||
string OsmOAuth::Request(ClientToken const & token, string const & method, string const & httpMethod, string const & body) const
|
||||
OsmOAuth::Response OsmOAuth::Request(ClientToken const & token, string const & method, string const & httpMethod, string const & body) const
|
||||
{
|
||||
CHECK(token.IsValid(), ("Empty request token"));
|
||||
OAuth::Consumer const consumer(m_consumerKey, m_consumerSecret);
|
||||
OAuth::Token const oatoken(token.m_key, token.m_secret);
|
||||
OAuth::Client oauth(&consumer, &oatoken);
|
||||
|
@ -215,22 +229,93 @@ string OsmOAuth::Request(ClientToken const & token, string const & method, strin
|
|||
reqType = OAuth::Http::Post;
|
||||
else if (httpMethod == "PUT")
|
||||
reqType = OAuth::Http::Put;
|
||||
else if (httpMethod == "DELETE")
|
||||
reqType = OAuth::Http::Delete;
|
||||
else
|
||||
{
|
||||
ASSERT(false, ("Unsupported OSM API request method", httpMethod));
|
||||
return string();
|
||||
return Response(ResponseCode::ServerError, string());
|
||||
}
|
||||
|
||||
string const url = m_apiUrl + kApiVersion + method;
|
||||
string const query = oauth.getURLQueryString(reqType, url, body);
|
||||
string const query = oauth.getURLQueryString(reqType, url);
|
||||
|
||||
HTTPClientPlatformWrapper request(url + "?" + query);
|
||||
if (httpMethod != "GET")
|
||||
request.set_body_data(body, "application/xml", httpMethod);
|
||||
if (request.RunHTTPRequest() && request.error_code() == 200 && !request.was_redirected())
|
||||
return request.server_response();
|
||||
if (!request.RunHTTPRequest() || request.was_redirected())
|
||||
return Response(ResponseCode::ServerError, string());
|
||||
return Response(static_cast<ResponseCode>(request.error_code()), request.server_response());
|
||||
}
|
||||
|
||||
return string();
|
||||
OsmOAuth::Response OsmOAuth::DirectRequest(string const & method, bool api) const
|
||||
{
|
||||
string const url = api ? m_apiUrl + kApiVersion + method : m_baseUrl + method;
|
||||
HTTPClientPlatformWrapper request(url);
|
||||
if (!request.RunHTTPRequest())
|
||||
return Response(ResponseCode::ServerError, string());
|
||||
return Response(static_cast<ResponseCode>(request.error_code()), request.server_response());
|
||||
}
|
||||
|
||||
void OsmOAuth::SetToken(ClientToken const & token)
|
||||
{
|
||||
m_token.m_key = token.m_key;
|
||||
m_token.m_secret = token.m_secret;
|
||||
}
|
||||
|
||||
OsmOAuth::AuthResult OsmOAuth::FetchAccessToken(SessionID const & sid)
|
||||
{
|
||||
return FetchAccessToken(sid, m_token);
|
||||
}
|
||||
|
||||
OsmOAuth::AuthResult OsmOAuth::AuthorizePassword(string const & login, string const & password)
|
||||
{
|
||||
return AuthorizePassword(login, password, m_token);
|
||||
}
|
||||
|
||||
OsmOAuth::AuthResult OsmOAuth::AuthorizeFacebook(string const & facebookToken)
|
||||
{
|
||||
return AuthorizeFacebook(facebookToken, m_token);
|
||||
}
|
||||
|
||||
OsmOAuth::Response OsmOAuth::Request(string const & method, string const & httpMethod, string const & body) const
|
||||
{
|
||||
return Request(m_token, method, httpMethod, body);
|
||||
}
|
||||
|
||||
string DebugPrint(OsmOAuth::AuthResult const res)
|
||||
{
|
||||
switch (res)
|
||||
{
|
||||
case OsmOAuth::AuthResult::OK: return "OK";
|
||||
case OsmOAuth::AuthResult::FailCookie: return "FailCookie";
|
||||
case OsmOAuth::AuthResult::FailLogin: return "FailLogin";
|
||||
case OsmOAuth::AuthResult::NoOAuth: return "NoOAuth";
|
||||
case OsmOAuth::AuthResult::FailAuth: return "FailAuth";
|
||||
case OsmOAuth::AuthResult::NoAccess: return "NoAccess";
|
||||
case OsmOAuth::AuthResult::NetworkError: return "NetworkError";
|
||||
case OsmOAuth::AuthResult::ServerError: return "ServerError";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
string DebugPrint(OsmOAuth::ResponseCode const code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case OsmOAuth::ResponseCode::ServerError: return "ServerError";
|
||||
case OsmOAuth::ResponseCode::OK: return "OK";
|
||||
case OsmOAuth::ResponseCode::BadXML: return "BadXML";
|
||||
case OsmOAuth::ResponseCode::Redacted: return "Redacted";
|
||||
case OsmOAuth::ResponseCode::NotFound: return "NotFound";
|
||||
case OsmOAuth::ResponseCode::WrongMethod: return "WrongMethod";
|
||||
case OsmOAuth::ResponseCode::Conflict: return "Conflict";
|
||||
case OsmOAuth::ResponseCode::Gone: return "Gone";
|
||||
case OsmOAuth::ResponseCode::RefError: return "RefError";
|
||||
case OsmOAuth::ResponseCode::URITooLong: return "URITooLong";
|
||||
case OsmOAuth::ResponseCode::TooMuchData: return "TooMuchData";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
} // namespace osm
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
namespace osm
|
||||
{
|
||||
|
||||
/// A structure that holds request token pair.
|
||||
struct ClientToken
|
||||
{
|
||||
string m_key;
|
||||
|
@ -18,7 +19,8 @@ constexpr char const * kDefaultApiURL = "https://api.openstreetmap.org";
|
|||
class OsmOAuth
|
||||
{
|
||||
public:
|
||||
enum AuthResult
|
||||
/// A result of authentication. OK if everything is good.
|
||||
enum class AuthResult
|
||||
{
|
||||
OK,
|
||||
FailCookie,
|
||||
|
@ -30,19 +32,53 @@ public:
|
|||
ServerError
|
||||
};
|
||||
|
||||
/// A result of a request. Has readable values for all OSM API return codes.
|
||||
enum class ResponseCode
|
||||
{
|
||||
ServerError = -1,
|
||||
OK = 200,
|
||||
BadXML = 400,
|
||||
Redacted = 403,
|
||||
NotFound = 404,
|
||||
WrongMethod = 405,
|
||||
Conflict = 409,
|
||||
Gone = 410,
|
||||
RefError = 412,
|
||||
URITooLong = 414,
|
||||
TooMuchData = 509
|
||||
};
|
||||
|
||||
/// A pair of <error code, response contents>
|
||||
using Response = std::pair<ResponseCode, string>;
|
||||
|
||||
/// The constructor. Simply stores a lot of strings in fields.
|
||||
/// @param[apiUrl] The OSM API URL defaults to baseUrl, or kDefaultApiURL if not specified.
|
||||
OsmOAuth(string const & consumerKey, string const & consumerSecret,
|
||||
string const & baseUrl = kDefaultBaseURL,
|
||||
string const & apiUrl = kDefaultApiURL):
|
||||
m_consumerKey(consumerKey),
|
||||
m_consumerSecret(consumerSecret),
|
||||
m_baseUrl(baseUrl),
|
||||
m_apiUrl(apiUrl)
|
||||
{
|
||||
}
|
||||
string const & apiUrl = kDefaultApiURL);
|
||||
|
||||
/// @name Stateless methods.
|
||||
//@{
|
||||
AuthResult AuthorizePassword(string const & login, string const & password, ClientToken & token) const;
|
||||
AuthResult AuthorizeFacebook(string const & facebookToken, ClientToken & token) const;
|
||||
string Request(ClientToken const & token, string const & method, string const & httpMethod = "GET", string const & body = "") const;
|
||||
/// @param[method] The API method, must start with a forward slash.
|
||||
Response Request(ClientToken const & token, string const & method, string const & httpMethod = "GET", string const & body = "") const;
|
||||
//@}
|
||||
|
||||
/// @name Methods for using a token stored in this class. Obviously not thread-safe.
|
||||
//@{
|
||||
void SetToken(ClientToken const & token);
|
||||
ClientToken const & GetToken() const { return m_token; }
|
||||
bool IsAuthorized() const { return m_token.IsValid(); }
|
||||
AuthResult AuthorizePassword(string const & login, string const & password);
|
||||
AuthResult AuthorizeFacebook(string const & facebookToken);
|
||||
/// @param[method] The API method, must start with a forward slash.
|
||||
Response Request(string const & method, string const & httpMethod = "GET", string const & body = "") const;
|
||||
//@}
|
||||
|
||||
/// Tokenless GET request, for convenience.
|
||||
/// @param[api] If false, request is made to m_baseUrl.
|
||||
Response DirectRequest(string const & method, bool api = true) const;
|
||||
|
||||
private:
|
||||
struct SessionID
|
||||
|
@ -55,6 +91,7 @@ private:
|
|||
string m_consumerSecret;
|
||||
string m_baseUrl;
|
||||
string m_apiUrl;
|
||||
ClientToken m_token;
|
||||
|
||||
AuthResult FetchSessionId(SessionID & sid) const;
|
||||
AuthResult LogoutUser(SessionID const & sid) const;
|
||||
|
@ -62,6 +99,10 @@ private:
|
|||
AuthResult LoginFacebook(string const & facebookToken, SessionID const & sid) const;
|
||||
string SendAuthRequest(string const & requestTokenKey, SessionID const & sid) const;
|
||||
AuthResult FetchAccessToken(SessionID const & sid, ClientToken & token) const;
|
||||
AuthResult FetchAccessToken(SessionID const & sid);
|
||||
};
|
||||
|
||||
string DebugPrint(OsmOAuth::AuthResult const res);
|
||||
string DebugPrint(OsmOAuth::ResponseCode const code);
|
||||
|
||||
} // namespace osm
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "editor/server_api.hpp"
|
||||
|
||||
#include "coding/url_encode.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
@ -7,30 +9,22 @@
|
|||
|
||||
#include "std/sstream.hpp"
|
||||
|
||||
#include "3party/Alohalytics/src/http_client.h"
|
||||
|
||||
using alohalytics::HTTPClientPlatformWrapper;
|
||||
|
||||
namespace osm
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
void PrintRequest(HTTPClientPlatformWrapper const & r)
|
||||
{
|
||||
LOG(LINFO, ("HTTP", r.http_method(), r.url_requested(), "has finished with code", r.error_code(),
|
||||
(r.was_redirected() ? ", was redirected to " + r.url_received() : ""),
|
||||
"Server replied:\n", r.server_response()));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ServerApi06::ServerApi06(string const & user, string const & password, string const & baseUrl)
|
||||
: m_user(user), m_password(password), m_baseOsmServerUrl(baseUrl)
|
||||
ServerApi06::ServerApi06(OsmOAuth & auth)
|
||||
: m_auth(auth)
|
||||
{
|
||||
}
|
||||
|
||||
bool ServerApi06::CreateChangeSet(TKeyValueTags const & kvTags, uint64_t & outChangeSetId) const
|
||||
{
|
||||
if (!m_auth.IsAuthorized())
|
||||
{
|
||||
LOG(LWARNING, ("Not authorized"));
|
||||
return false;
|
||||
}
|
||||
|
||||
ostringstream stream;
|
||||
stream << "<osm>\n"
|
||||
"<changeset>\n";
|
||||
|
@ -39,114 +33,72 @@ bool ServerApi06::CreateChangeSet(TKeyValueTags const & kvTags, uint64_t & outCh
|
|||
stream << "</changeset>\n"
|
||||
"</osm>\n";
|
||||
|
||||
HTTPClientPlatformWrapper request(m_baseOsmServerUrl + "/api/0.6/changeset/create");
|
||||
bool const success = request.set_user_and_password(m_user, m_password)
|
||||
.set_body_data(move(stream.str()), "", "PUT")
|
||||
.RunHTTPRequest();
|
||||
if (success && request.error_code() == 200)
|
||||
OsmOAuth::Response const response = m_auth.Request("/changeset/create", "PUT", move(stream.str()));
|
||||
if (response.first == OsmOAuth::ResponseCode::OK)
|
||||
{
|
||||
if (strings::to_uint64(request.server_response(), outChangeSetId))
|
||||
if (strings::to_uint64(response.second, outChangeSetId))
|
||||
return true;
|
||||
LOG(LWARNING, ("Can't parse changeset ID from server response."));
|
||||
}
|
||||
else
|
||||
LOG(LWARNING, ("CreateChangeSet request has failed."));
|
||||
LOG(LWARNING, ("CreateChangeSet request has failed:", response.first));
|
||||
|
||||
PrintRequest(request);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ServerApi06::CreateNode(string const & nodeXml, uint64_t & outCreatedNodeId) const
|
||||
{
|
||||
HTTPClientPlatformWrapper request(m_baseOsmServerUrl + "/api/0.6/node/create");
|
||||
bool const success = request.set_user_and_password(m_user, m_password)
|
||||
.set_body_data(move(nodeXml), "", "PUT")
|
||||
.RunHTTPRequest();
|
||||
if (success && request.error_code() == 200)
|
||||
OsmOAuth::Response const response = m_auth.Request("/node/create", "PUT", move(nodeXml));
|
||||
if (response.first == OsmOAuth::ResponseCode::OK)
|
||||
{
|
||||
if (strings::to_uint64(request.server_response(), outCreatedNodeId))
|
||||
if (strings::to_uint64(response.second, outCreatedNodeId))
|
||||
return true;
|
||||
LOG(LWARNING, ("Can't parse created node ID from server response."));
|
||||
}
|
||||
else
|
||||
LOG(LWARNING, ("CreateNode request has failed."));
|
||||
LOG(LWARNING, ("CreateNode request has failed:", response.first));
|
||||
|
||||
PrintRequest(request);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ServerApi06::ModifyNode(string const & nodeXml, uint64_t nodeId) const
|
||||
{
|
||||
HTTPClientPlatformWrapper request(m_baseOsmServerUrl + "/api/0.6/node/" + strings::to_string(nodeId));
|
||||
bool const success = request.set_user_and_password(m_user, m_password)
|
||||
.set_body_data(move(nodeXml), "", "PUT")
|
||||
.RunHTTPRequest();
|
||||
if (success && request.error_code() == 200)
|
||||
OsmOAuth::Response const response = m_auth.Request("/node/" + strings::to_string(nodeId), "PUT", move(nodeXml));
|
||||
if (response.first == OsmOAuth::ResponseCode::OK)
|
||||
return true;
|
||||
|
||||
LOG(LWARNING, ("ModifyNode request has failed."));
|
||||
PrintRequest(request);
|
||||
LOG(LWARNING, ("ModifyNode request has failed:", response.first));
|
||||
return false;
|
||||
}
|
||||
|
||||
ServerApi06::DeleteResult ServerApi06::DeleteNode(string const & nodeXml, uint64_t nodeId) const
|
||||
{
|
||||
HTTPClientPlatformWrapper request(m_baseOsmServerUrl + "/api/0.6/node/" + strings::to_string(nodeId));
|
||||
bool const success = request.set_user_and_password(m_user, m_password)
|
||||
.set_body_data(move(nodeXml), "", "DELETE")
|
||||
.RunHTTPRequest();
|
||||
if (success)
|
||||
{
|
||||
switch (request.error_code())
|
||||
{
|
||||
case 200: return DeleteResult::ESuccessfullyDeleted;
|
||||
case 412: return DeleteResult::ECanNotBeDeleted;
|
||||
}
|
||||
}
|
||||
OsmOAuth::Response const response = m_auth.Request("/node/" + strings::to_string(nodeId), "DELETE", move(nodeXml));
|
||||
if (response.first == OsmOAuth::ResponseCode::OK)
|
||||
return DeleteResult::ESuccessfullyDeleted;
|
||||
else if (static_cast<int>(response.first) >= 400)
|
||||
// Tons of reasons, see http://wiki.openstreetmap.org/wiki/API_v0.6#Error_codes_16
|
||||
return DeleteResult::ECanNotBeDeleted;
|
||||
|
||||
LOG(LWARNING, ("DeleteNode request has failed."));
|
||||
PrintRequest(request);
|
||||
LOG(LWARNING, ("DeleteNode request has failed:", response.first));
|
||||
return DeleteResult::EFailed;
|
||||
}
|
||||
|
||||
bool ServerApi06::CloseChangeSet(uint64_t changesetId) const
|
||||
{
|
||||
HTTPClientPlatformWrapper request(m_baseOsmServerUrl + "/api/0.6/changeset/" +
|
||||
strings::to_string(changesetId) + "/close");
|
||||
bool const success = request.set_user_and_password(m_user, m_password)
|
||||
.set_http_method("PUT")
|
||||
.RunHTTPRequest();
|
||||
if (success && request.error_code() == 200)
|
||||
OsmOAuth::Response const response = m_auth.Request("/changeset/" + strings::to_string(changesetId) + "/close", "PUT");
|
||||
if (response.first == OsmOAuth::ResponseCode::OK)
|
||||
return true;
|
||||
|
||||
LOG(LWARNING, ("CloseChangeSet request has failed."));
|
||||
PrintRequest(request);
|
||||
LOG(LWARNING, ("CloseChangeSet request has failed:", response.first));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ServerApi06::CheckUserAndPassword() const
|
||||
bool ServerApi06::TestUserExists(string const & userName)
|
||||
{
|
||||
static constexpr char const * kAPIWritePermission = "allow_write_api";
|
||||
HTTPClientPlatformWrapper request(m_baseOsmServerUrl + "/api/0.6/permissions");
|
||||
bool const success = request.set_user_and_password(m_user, m_password).RunHTTPRequest();
|
||||
if (success && request.error_code() == 200 &&
|
||||
request.server_response().find(kAPIWritePermission) != string::npos)
|
||||
return true;
|
||||
|
||||
LOG(LWARNING, ("OSM user and/or password are invalid."));
|
||||
PrintRequest(request);
|
||||
return false;
|
||||
}
|
||||
|
||||
int ServerApi06::HttpCodeForUrl(string const & url)
|
||||
{
|
||||
HTTPClientPlatformWrapper request(url);
|
||||
bool const success = request.RunHTTPRequest();
|
||||
int const httpCode = request.error_code();
|
||||
if (success)
|
||||
return httpCode;
|
||||
|
||||
return -1;
|
||||
string const method = "/user/" + UrlEncode(userName);
|
||||
OsmOAuth::Response const response = m_auth.DirectRequest(method, false);
|
||||
return response.first == OsmOAuth::ResponseCode::OK;
|
||||
}
|
||||
|
||||
string ServerApi06::GetXmlFeaturesInRect(m2::RectD const & latLonRect) const
|
||||
|
@ -157,15 +109,14 @@ string ServerApi06::GetXmlFeaturesInRect(m2::RectD const & latLonRect) const
|
|||
static constexpr double const kDAC = 7;
|
||||
m2::PointD const lb = latLonRect.LeftBottom();
|
||||
m2::PointD const rt = latLonRect.RightTop();
|
||||
string const url = m_baseOsmServerUrl + "/api/0.6/map?bbox=" + to_string_dac(lb.x, kDAC) + ',' + to_string_dac(lb.y, kDAC) + ',' +
|
||||
string const url = "/map?bbox=" + to_string_dac(lb.x, kDAC) + ',' + to_string_dac(lb.y, kDAC) + ',' +
|
||||
to_string_dac(rt.x, kDAC) + ',' + to_string_dac(rt.y, kDAC);
|
||||
HTTPClientPlatformWrapper request(url);
|
||||
bool const success = request.set_user_and_password(m_user, m_password).RunHTTPRequest();
|
||||
if (success && request.error_code() == 200)
|
||||
return request.server_response();
|
||||
|
||||
LOG(LWARNING, ("GetXmlFeaturesInRect request has failed."));
|
||||
PrintRequest(request);
|
||||
OsmOAuth::Response const response = m_auth.DirectRequest(url);
|
||||
if (response.first == OsmOAuth::ResponseCode::OK)
|
||||
return response.second;
|
||||
|
||||
LOG(LWARNING, ("GetXmlFeaturesInRect request has failed:", response.first));
|
||||
return string();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "editor/osm_auth.hpp"
|
||||
|
||||
#include "geometry/rect2d.hpp"
|
||||
|
||||
#include "std/map.hpp"
|
||||
|
@ -24,14 +26,9 @@ public:
|
|||
ECanNotBeDeleted
|
||||
};
|
||||
|
||||
ServerApi06(string const & user, string const & password, string const & baseUrl = "http://api.openstreetmap.org");
|
||||
/// @returns true if connection with OSM server was established, and user+password are valid.
|
||||
bool CheckUserAndPassword() const;
|
||||
/// @returns http server code for given url or negative value in case of error.
|
||||
ServerApi06(OsmOAuth & auth);
|
||||
/// This function can be used to check if user did not confirm email validation link after registration.
|
||||
/// For example, for http://www.openstreetmap.org/user/UserName 200 is returned if UserName was registered.
|
||||
static int HttpCodeForUrl(string const & url);
|
||||
|
||||
bool TestUserExists(string const & userName);
|
||||
/// Please use at least created_by=* and comment=* tags.
|
||||
bool CreateChangeSet(TKeyValueTags const & kvTags, uint64_t & outChangeSetId) const;
|
||||
/// nodeXml should be wrapped into <osm> ... </osm> tags.
|
||||
|
@ -47,9 +44,7 @@ public:
|
|||
string GetXmlNodeByLatLon(double lat, double lon) const;
|
||||
|
||||
private:
|
||||
string m_user;
|
||||
string m_password;
|
||||
string m_baseOsmServerUrl;
|
||||
OsmOAuth m_auth;
|
||||
};
|
||||
|
||||
} // namespace osm
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
static NSString * const kOSMUsernameKey = @"OSMUsernameKey";
|
||||
static NSString * const kOSMPasswordKey = @"OSMPasswordKey";
|
||||
static NSString * const kOSMRequestToken = @"OSMRequestToken";
|
||||
static NSString * const kOSMRequestSecret = @"OSMRequestSecret";
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, MWMAuthorizationButtonType)
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#import "UIColor+MapsMeColor.h"
|
||||
#import "UITextField+RuntimeAttributes.h"
|
||||
|
||||
#include "private.h"
|
||||
#include "editor/server_api.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
|
@ -98,11 +99,13 @@ typedef NS_OPTIONS(NSUInteger, MWMFieldCorrect)
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (void)storeCredentials
|
||||
- (void)storeCredentials:(osm::ClientToken const *)token
|
||||
{
|
||||
NSString * requestToken = @(token->m_key.c_str());
|
||||
NSString * requestSecret = @(token->m_secret.c_str());
|
||||
NSUserDefaults * ud = [NSUserDefaults standardUserDefaults];
|
||||
[ud setObject:self.loginTextField.text forKey:kOSMUsernameKey];
|
||||
[ud setObject:self.passwordTextField.text forKey:kOSMPasswordKey];
|
||||
[ud setObject:requestToken forKey:kOSMRequestToken];
|
||||
[ud setObject:requestSecret forKey:kOSMRequestSecret];
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
|
@ -166,12 +169,14 @@ typedef NS_OPTIONS(NSUInteger, MWMFieldCorrect)
|
|||
{
|
||||
string const username = self.loginTextField.text.UTF8String;
|
||||
string const password = self.passwordTextField.text.UTF8String;
|
||||
BOOL const credentialsOK = osm::ServerApi06(username, password).CheckUserAndPassword();
|
||||
osm::OsmOAuth auth(OSM_CONSUMER_KEY, OSM_CONSUMER_SECRET);
|
||||
osm::ClientToken token;
|
||||
osm::OsmOAuth::AuthResult const result = auth.AuthorizePassword(username, password, token);
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self stopSpinner];
|
||||
if (credentialsOK)
|
||||
[self storeCredentials];
|
||||
if (result == osm::OsmOAuth::AuthResult::OK)
|
||||
[self storeCredentials:&token];
|
||||
else
|
||||
[self showInvalidCredentialsAlert];
|
||||
});
|
||||
|
@ -179,7 +184,8 @@ typedef NS_OPTIONS(NSUInteger, MWMFieldCorrect)
|
|||
}
|
||||
else
|
||||
{
|
||||
[self storeCredentials];
|
||||
// Not connected, cannot validate login/password.
|
||||
// TODO(@igrechuhin)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,9 +54,9 @@ extern NSDictionary * const kMWMOpeningHoursEditorTableCells = @{
|
|||
- (void)checkAuthorization
|
||||
{
|
||||
NSUserDefaults * ud = [NSUserDefaults standardUserDefaults];
|
||||
NSString * username = [ud stringForKey:kOSMUsernameKey];
|
||||
NSString * password = [ud stringForKey:kOSMPasswordKey];
|
||||
if (!username || !password)
|
||||
NSString * requestToken = [ud stringForKey:kOSMRequestToken];
|
||||
NSString * requestSecret = [ud stringForKey:kOSMRequestSecret];
|
||||
if (!requestToken || !requestSecret)
|
||||
{
|
||||
[[Statistics instance] logEvent:kStatEventName(kStatPlacePage, kStatEditTime)
|
||||
withParameters:@{kStatValue : kStatAuthorization}];
|
||||
|
|
|
@ -5219,6 +5219,7 @@
|
|||
"-ObjC",
|
||||
"-lopening_hours",
|
||||
"-lpugixml",
|
||||
"-loauthcpp",
|
||||
"-leditor",
|
||||
);
|
||||
PRODUCT_NAME = "maps.me dbg";
|
||||
|
@ -5341,6 +5342,7 @@
|
|||
"-ObjC",
|
||||
"-lopening_hours",
|
||||
"-lpugixml",
|
||||
"-loauthcpp",
|
||||
"-leditor",
|
||||
);
|
||||
PRODUCT_NAME = "maps.me dbg";
|
||||
|
@ -5465,6 +5467,7 @@
|
|||
"-ObjC",
|
||||
"-lopening_hours",
|
||||
"-lpugixml",
|
||||
"-loauthcpp",
|
||||
"-leditor",
|
||||
);
|
||||
PRODUCT_NAME = "maps.me beta";
|
||||
|
@ -5590,6 +5593,7 @@
|
|||
"-ObjC",
|
||||
"-lopening_hours",
|
||||
"-lpugixml",
|
||||
"-loauthcpp",
|
||||
"-leditor",
|
||||
);
|
||||
PRODUCT_NAME = maps.me;
|
||||
|
@ -5714,6 +5718,7 @@
|
|||
"-ObjC",
|
||||
"-lopening_hours",
|
||||
"-lpugixml",
|
||||
"-loauthcpp",
|
||||
"-leditor",
|
||||
);
|
||||
PRODUCT_NAME = "maps.me rel";
|
||||
|
@ -5836,6 +5841,7 @@
|
|||
"-ObjC",
|
||||
"-lopening_hours",
|
||||
"-lpugixml",
|
||||
"-loauthcpp",
|
||||
"-leditor",
|
||||
);
|
||||
PRODUCT_NAME = "maps.me rel";
|
||||
|
|
Loading…
Add table
Reference in a new issue