For our website, we do a lot of automated tests. So, recently I've just written a method using Facebook Graph API that create a wall post on feed. This method works when I test it live using real facebook accounts. However, when I use facebook test users (with permission set to "publish_stream"), then I get 403 forbidden.
Are "test users" not allowed to make wall post? or is there something that I am not doing right?
This is my test code written in groovy
void testPostMessageOnWall() {
def appAccessToken = facebookService.getAppAccessToken()
assertNotNull appAccessToken
def testUser1 = facebookService.createTestAccount("Ryoko UserOne", appAccessToken)
assertNotNull testUser1
def testUser2 = facebookService.createTestAccount("Ryoko UserTwo", appAccessToken)
assertNotNull testUser2
def response = facebookService.connectTwoTestAccount(testUser1.id, testUser1.access_token, testUser2.id, testUser2.access_token)
assertTrue response
println testUser1
println testUser2
def postResponse = facebookService.postMessageOnWall([accessToken:testUser1.access_token,
from:testUser1.id,
to:testUser2.id,
message:"Join ryoko.it. It's nice there!",
link:"http://ryoko.it",
name:"name of website",
caption:"ryoko.it",
description:"description",
picture:"http://ryoko.it/images/ryoko.png"
])
println postResponse
assertNotNull postResponse
facebookService.deleteTestAccount(testUser1.id, testUser1.access_token)
facebookService.deleteTestAccount(testUser2.id, testUser2.access_token)
}
This test makes two test users/accounts and make them friends of each other, then testUser1 post something in testUser2's wall. It fails in line: assertNotNull postResponse.
This is the header of the response:
Date: Thu, 01 Sep 2011 18:39:10 GMT
WWW-Authenticate: OAuth "Facebook Platform" "insufficient_scope" "(#200) The user hasn't authorized the application to perform this action"
P3P: CP="Facebook does not have a P3P policy. Learn why here: http://fb.me/p3p"
X-Cnection: close
X-FB-Rev: 433230
Content-Length: 146
Pragma: no-cache
X-FB-Server: 10.64.212.43
Content-Type: text/javascript; charset=UTF-8
Cache-Control: no-store
Expires: Sat, 01 Jan 2000 00:00:00 GMT
data:
{
"error": {
"type": "OAuthException",
"message": "(#200) The user hasn't authorized the application to perform this action"
}
}
The user is created as such:
def createTestAccount(fullname, appAccessToken) {
def accessToken = appAccessToken
def urlString = "${GRAPH_API_URL}/${APP_ID}/accounts/test-users?installed=true"
def encodedFullname = URLEncoder.encode(fullname, "UTF-8")
urlString += "&name=${encodedFullname}"
urlString += "&permission=create_note,email,offline_access,photo_upload,publish_stream,read_friendlists,share_item,status_update,video_upload"
urlString += "&method=post"
urlString += "&access_token=${accessToken}"
def url = new URL(urlString)
def connection = url.openConnection()
def userDetails
if (connection.responseCode == 200) {
userDetails = JSON.parse(connection.content.text)
}
else {
println "[FACEBOOK]\tResponse code ${connection.responseCode}: ${connection.responseMessage} [${urlString}]"
}
userDetails
}
and the post message goes like this:
def postMessageOnWall(params) {
assert params.accessToken
assert params.from
assert params.to
def content = "access_token=${postEncode(params.accessToken)}"
if (params.message) content += "&message=${postEncode(params.message)}"
if (params.link) content += "&link=${postEncode(params.link)}"
if (params.name) content += "&name=${postEncode(params.name)}"
if (params.caption) content += "&caption=${postEncode(params.caption)}"
if (params.description) content += "&description=${postEncode(params.description)}"
if (params.picture) content += "&picture=${postEncode(params.picture)}"
if (params.from) content += "&from=${postEncode(params.from)}"
if (params.to) content += "&to=${postEncode(params.to)}"
def urlString = "${GRAPH_API_URL}/${params.to}/feed"
def url = new URL(urlString)
def connection = url.openConnection()
connection.doOutput = true
connection.setRequestMethod("POST")
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
connection.setRequestProperty("Content-Length", "${content.size()}")
println content
def writer = new OutputStreamWriter(connection.outputStream)
writer.write(content)
writer.flush()
writer.close()
connection.connect()
def response
if (connection.responseCode == 200) {
response = JSON.parse(connection.content.text)
}
else {
println "[FACEBOOK]\tResponse code ${connection.responseCode}: ${connection.responseMessage} [${urlString}]"
}
println "response: ${response}"
response
}
Even though it works using real facebook accounts (by filling in the id and access token manually), this still bothers me. I'm genuinely curious about you think the problem might be.
In your createTestAccount(), the query parameter 'permission' should be 'permissions' (in plural form).
Related
Currently running tests for my REST API which:
takes an endpoint from the user
using that endpoint, grabs info from a server
sends it to another server to be translated
then proceeds to jsonify the data.
I've written a series of automated tests running and I cannot get one to pass - the test that actually identifies the content of the response. I've tried including several variations of what the test is expecting but I feel it's the actual implementation that's the issue. Here's the expected API response from the client request:
{ "name": "random_character", "description": "Translated description of requested character is output here" }
Here is the testing class inside my test_main.py:
class Test_functions(unittest.TestCase):
# checking if response of 200 is returned
def test_healthcheck_PokeAPI(self):
manualtest = app.test_client(self)
response = manualtest.get("/pokemon/")
status_code = response.status_code
self.assertEqual(status_code, 200)
# the status code should be a redirect i.e. 308; so I made a separate test for this
def test_healthcheck_ShakesprAPI(self):
manualtest = app.test_client(self)
response = manualtest.get("/pokemon/charizard")
self.assertEqual(response.status_code, 308)
def test_response_content(self):
manualtest = app.test_client(self)
response = manualtest.get("/pokemon/charizard")
self.assertEqual(response.content_type,
'application/json') <<<< this test is failing
def test_trans_shakespeare_response(self):
manualtest = app.test_client(self)
response = manualtest.get("/pokemon/charizard")
self.assertFalse(b"doth" in response.data)
Traceback:
AssertionError: 'text/html; charset=utf-8' != 'application/json' - text/html; charset=utf-8 + application/json
Any help would be greatly appreciated
I tried TFS 2015 REST API Authentication
However, it mentions request object (as I can't use javascript), not sure where is the request object or what type of it.
I am trying to pass query id and the code should execute the query and get result via API.
The solution works from my local, however, after publishing to server it does not seems working.
I also checked that the TFS is accessible from server using the credentials.
My code below:
private HttpClientHandler GetTfsCredentials()
{
HttpClientHandler handler2 = new HttpClientHandler { UseDefaultCredentials = true };
handler2.Credentials = new NetworkCredential("username", "password", "domain");
return handler2;
}
private async Task<object> GetQueryResults(string queryId)
{
string tfsApiUrl = ConfigurationManager.AppSettings["TfsApiUrl"];
string tfsProjectName = ConfigurationManager.AppSettings["TfsProjectName"];
string TfsProjectGuid = ConfigurationManager.AppSettings["TfsProjectGuid"];
//I tried both credentials and credentials2, but none of them working
string credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes($"{""}:{"password"}"));
string credentials2 = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("domain\\username:password") );
if (!string.IsNullOrEmpty(tfsApiUrl) && !string.IsNullOrEmpty(tfsProjectName)
&& !string.IsNullOrEmpty(Id))
{
log.Info("GetQueryResults:: Config values found");
using (var client = new HttpClient(GetTfsCredentials()) { BaseAddress = new Uri(tfsApiUrl) })
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials2);
HttpResponseMessage response = client.GetAsync($"{tfsProjectName}/_apis/wit/wiql/{Id}").Result;
log.Info("GetQueryResults:: response.ReasonPhrase" + response.ReasonPhrase.ToString());
log.Info("GetQueryResults:: response" + response.ToString());
log.Info("GetQueryResults:: response.IsSuccessStatusCode" + response.IsSuccessStatusCode.ToString());
string workItemList = null;
if (response.IsSuccessStatusCode)
{
//do something
}
}
}
return null;
}
The error I received is:
2020-03-20 16:17:35,382 INFO GetQueryResults:: response.ReasonPhrase Unauthorized
2020-03-20 16:17:35,382 INFO GetQueryResults:: responseStatus Code: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
X-TFS-ProcessId: 115b5bba-0bf4-45e2-a3b2-2913ccc93f09
ActivityId: bb21d947-99a3-44dc-bdb7-317d7af34934
X-TFS-Session: bb21d947-99a3-44dc-bdb7-317d7af34934
X-VSS-E2EID: bb21d947-99a3-44dc-bdb7-317d7af34934
X-FRAME-OPTIONS: SAMEORIGIN
X-TFS-SoapException: %3c%3fxml+version%3d%221.0%22+encoding%3d%22utf-8%22%3f%3e%3csoap%3aEnvelope+xmlns%3asoap%3d%22http%3a%2f%2fwww.w3.org%2f2003%2f05%2fsoap-envelope%22%3e%3csoap%3aBody%3e%3csoap%3aFault%3e%3csoap%3aCode%3e%3csoap%3aValue%3esoap%3aReceiver%3c%2fsoap%3aValue%3e%3csoap%3aSubcode%3e%3csoap%3aValue%3eUnauthorizedRequestException%3c%2fsoap%3aValue%3e%3c%2fsoap%3aSubcode%3e%3c%2fsoap%3aCode%3e%3csoap%3aReason%3e%3csoap%3aText+xml%3alang%3d%22en%22%3eTF400813%3a+The+user+%27CWOPA%5cSTCTCAPD006%24%27+is+not+authorized+to+access+this+resource.%3c%2fsoap%3aText%3e%3c%2fsoap%3aReason%3e%3c%2fsoap%3aFault%3e%3c%2fsoap%3aBody%3e%3c%2fsoap%3aEnvelope%3e
X-TFS-ServiceError: TF400813%3a+The+user+%27CWOPA%5cSTCTCAPD006%24%27+is+not+authorized+to+access+this+resource.
Server: Microsoft-IIS/8.5
WWW-Authenticate: Bearer
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
P3P: CP="CAO DSP COR ADMa DEV CONo TELo CUR PSA PSD TAI IVDo OUR SAMi BUS DEM NAV STA UNI COM INT PHY ONL FIN PUR LOC CNT"
Lfs-Authenticate: NTLM
X-Content-Type-Options: nosniff
Date: Fri, 20 Mar 2020 20:17:35 GMT
Content-Length: 82
Content-Type: text/plain; charset=utf-8
}
2020-03-20 16:17:35,382 INFO GetQueryResults:: response.IsSuccessStatusCode False
It looks like you are doing authentication in two different ways at once:
In the GetTfsCredentials-Method you set up Windows Authentication (NTLM or Kerberos)
By adding client.DefaultRequestHeaders.Authorization your try to set up Basic Authentication
Your TFS indicates (see WWW-Authenticate Header) that it supports Bearer, Negotiate and NTLM; but not Basic.
I would try:
Remove client.DefaultRequestHeaders.Authorization, credentials and credentials2. This should remove Basic-Authentication
Remove UseDefaultCredentials = true since you set explicit credentials the next line. UseDefaultCredentials tells HttpClientHandler to access TFS with the credentials of the running process, which is probably your account when executing locally and a service account when executing on the server.
Whithout this line, the specified NetworkCredential should be used to access TFS.
I call an API to get a zip file response. The API responds correctly but I am unable to get the byte array from response because the future that should complete on getting the ByteString never completes:
val authorization = akka.http.javadsl.model.headers.Authorization.basic("xxxxx", "xxxxxx")
val query = Map("fed" -> "xxxx", "trd" -> "yyy", "id" -> "zzz")
val request = HttpRequest(HttpMethods.GET, Uri("https://xxxx.yyyy.com/ggg/ttt.php").withQuery(Query(params = query))).addHeader(authorization)
val responseFut = http.singleRequest(request)
responseFut1.map(response => {
println("*******************************")
println(response)
response.status match {
case akka.http.javadsl.model.StatusCodes.OK => {
println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" + response._3)
val entityFut = response.entity.toStrict(60.seconds)
val byteStringFut = entityFut.flatMap(entity => {
entity.dataBytes.runFold(ByteString.empty)(_ ++ _)
})
println("#############################")
try {
byteStringFut.map(x => {
//this never prints =======================================problem
println("----------------------------" + x.toArray[Byte])
})
}catch{
case e: Exception => println("Error: " + e)
}
}
case _ => {}
}
})
If I print out the response this is what it looks like:
*******************************
HttpResponse(200 OK,List(Date: Fri, 08 Sep 2017 20:58:43 GMT, Server: Apache/2.4.18 (Ubuntu), Content-Disposition: attachment; filename="xxxxx.zip", Pragma: public, Cache-Contr
ol: public, must-revalidate, Content-Transfer-Encoding: binary),HttpEntity.Chunked(application/x-zip),HttpProtocol(HTTP/1.1))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^HttpEntity.Chunked(application/x-zip)
#############################
So response is coming back fine, but I still cannot get the binary data for the zip file.
We use akka-http in other places to call APIs that return json response and this approach seems to work fine there.
Why doesnt it work here? What am I doing wrong?
Any advice is appreciated.
Thanks.
Update
Adding byteStringFut.failed.foreach(println(_)) shows this exception: akka.http.scaladsl.model.EntityStreamException: HTTP chunk size exceeds the configured limit of 1048576 bytes
It looks like something went wrong and an exception was thrown in the async computation. You can check the exception by inspecting Future in the following way:
byteStringFut.failed.foreach(println)
------Update
Was able to fix it by using the UsernamePasswordCredentials class
The code looks like below
val client = new DefaultHttpClient
client.getCredentialsProvider().setCredentials(AuthScope.ANY,new UsernamePasswordCredentials("user","password"));
i am trying to make a HttpPost call to a Restful API, its expecting a username/password, how to pass those parameters? I tried 2 ways
post.addHeader("Username","user")
post.addHeader("Password","clear pwd")
and
post.addHeader("Authorization","Basic base64encoded username:password")
nothing works, I get response text as
Response Text = HTTP/1.1 401 Unauthorized [WWW-Authenticate: Digest realm="API Realm", domain="/default-api", nonce="pOxqalJKm5L5QXiphgFNmrtaJsh+gU", algorithm=MD5, qop="auth", stale=true, Content-Type: text/html; charset=ISO-8859-1, Cache-Control: must-revalidate,no-cache,no-store, Content-Length: 311] org.apache.http.conn.BasicManagedEntity#5afa04c
Below is my code
val url = "http://restapi_url";
val post = new HttpPost(url)
//post.addHeader("Authorization","Basic QWBX3VzZXI6Q0NBQGRidHMxMjM=")
post.addHeader("Username","user_user")
post.addHeader("Password","clear pwd")
post.addHeader("APPLICATION_NAME","DO")
val fileContents = Source.fromFile("input.xml").getLines.mkString
post.setHeader("Content-type", "application/xml")
post.setEntity(new StringEntity(fileContents))
val response = (new DefaultHttpClient).execute(post)
println("Response Text = "+response.toString())
// print the response headers
println("--- HEADERS ---")
response.getAllHeaders.foreach(arg => println(arg))
Here the authorization header should be calculated like this:
httpPost.addHeader("Authorization", "Basic " + Base64.getEncoder.encodeToString("[your-username]:[your-password]".getBytes))
Instead of getUrlEncoder(), it should be getEncoder().
you can write like this, it works in my program
import java.util.Base64
httpPost.addHeader("Authorization", "Basic " + Base64.getUrlEncoder.encodeToString("[your-username]:[your-password]".getBytes))
DefaultHttpClient is deprecated. You should use BasicCredentialsProvider instead. Example code below:
val username = "your_username"
val password = "your_password"
val credentialsProvider = new BasicCredentialsProvider()
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(username, password)
)
val httpClient = HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider).build()
I'm trying to connect my asp.net REST api to salesforce. I'm succesfully going through authentification, but when I start to send POST requests, I'm getting an error
{"errorCode":"INVALID_SESSION_ID","message":"Session expired or invalid"}
Here is my POST request:
//SFServerUrl = "https://na17.salesforce.com/services/";
//url = "data/v28.0/sobjects/Account";
ASCIIEncoding ascii = new ASCIIEncoding();
byte[] postBytes = ascii.GetBytes(postBody);
HttpWebRequest request = WebRequest.Create(Globals.SFServerUrl + url) as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = postBytes.Length;
Stream postStream = request.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
HttpCookie cookie = HttpContext.Current.Request.Cookies[Globals.SFCookie];
var ticket = FormsAuthentication.Decrypt(cookie.Value);
string authToken = ticket.UserData;
request.Headers.Add("Authorization", "Bearer " + authToken);
postStream.Close();
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
StringBuilder sb = new StringBuilder();
byte[] buf = new byte[8192];
Stream resStream = response.GetResponseStream();
string tempString = null;
int count = 0;
do
{
count = resStream.Read(buf, 0, buf.Length);
if (count != 0)
{
tempString = Encoding.ASCII.GetString(buf, 0, count);
sb.Append(tempString);
}
}
while (count > 0);
return new Tuple<bool, string>(true, sb.ToString());
When I'm trying to send GET request - I recieve 200 response.
Also, I've tried to send a POST Request with the same token from Simple Rest Client and it get's 200 response. I tried to change my "Authorization : Bearer" Header to "Authorization : Oauth", but nothing changed. I also tried to catch this error, get refresh token and send a request again with refreshed token, but nothing changed. Please, help me with this.
Using workbench I was able to POST the following JSON to /services/data/v29.0/sobjects/Account and create a new Account.
{
"Name" : "Express Logistics and Transport"
}
Raw Response
HTTP/1.1 201 Created
Date: Sun, 07 Sep 2014 21:32:06 GMT
Set-Cookie: BrowserId=_HC-bzpTQABC1237vFu2hA;Path=/;Domain=.salesforce.com;Expires=Thu, 06-Nov-2014 21:32:06 GMT
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Sforce-Limit-Info: api-usage=209/15000
Location: /services/data/v29.0/sobjects/Account/0010000000000001AAA
Content-Type: application/json;charset=UTF-8
Content-Encoding: gzip
Transfer-Encoding: chunked
{
"id" : "0010000000000001AAA",
"success" : true,
"errors" : [ ]
}
Things to check:
Your URL. It appears to be missing the leading /services
Is SFServerUrl the same Salesforce pod/server that the Session Id was issued to? If the Session Id came from another pod then it would be invalid on na17.
How did you create the Session Id? If you used OAuth, what scopes did you request?
Is the Session Id coming out of the cookie valid?
Has something else using the same Session Id called logout and invalidated the session?
Incidentally, the Salesforce StackExchange site is a great place to ask Salesforce specific questions.
See also:
Using REST API Resources - Create a Record
The problem was that I added Headers after Content. When I switched these lines of code everything worked.