Pregunta

I am having a problem getting information from Cloudant.com using a key/pass with read permissions (or all permissions). I was receiving a 500 error any way I would setup this user. However, currently everything is working fine, but I have left myself open for hacking, as I have to have my database open to be read by everyone. And we all know that can be a problem. I was wondering if anyone has any insight as to why this problem happens. BTW, I have tried it in both Android and iOS applications. This current question is using examples from an Android app.

Here is the criteria from my singleton Utopia:

private static String _remote_db_protocal = "https";
private static String _remote_db_key = "mykey";
private static String _remote_db_pass = "mypass";
private static String _remote_db_account = "myaccount";
private static String _remote_db_dbname = "mydbname";

public static String REMOTE_JSON_DB_URL = _remote_db_protocal+"://"+
        _remote_db_key+":"+_remote_db_pass+"@"+
        _remote_db_account+".cloudant.com/"+_remote_db_dbname;

Here is the information I am sending to get a URL String response:

    private static String dbBaseURL = Utopia.REMOTE_JSON_DB_URL;
    .... 
    String dbQueryURL = "/_design/app/_view/toys";
    URL finalURL;
    try {

        finalURL = new URL(dbBaseURL + dbQueryURL);
        Log.i(TAG, "Getting URL: "+finalURL.toString());

        response = WebStuff.getURLStringResponse(finalURL);
   ....

And lastly, here is my getURLStringResponse() function:

/*
 *  Get information back from a URL
 */
public static String getURLStringResponse(URL url)
{
    String response = "";

    try {
        URLConnection conn = url.openConnection();
        BufferedInputStream bin = new BufferedInputStream(conn.getInputStream());

        byte[] contentBytes = new byte[1024];
        int bytesRead = 0;
        StringBuffer responseBuffer = new StringBuffer();

        while ((bytesRead = bin.read(contentBytes)) != -1)
        {
            response = new String(contentBytes, 0, bytesRead);
            responseBuffer.append(response);
        }

        return responseBuffer.toString();

    } catch (Exception e) {
        // TODO: handle exception
        Log.e(TAG, "getURLStringResponse Error as Follows");
        Log.e(TAG, e.toString());
    }

    return response;
}

So with all of the above, I hope someone can answer the problem. I must also note, that I am to take my exact URL from the query that gets spit out in the log somewhere, or just recreate it from the REMOTE_JSON_DB_URL variable and values of what i'm looking for, and past that into the browser, everything worked fine (yes, being logged off of my account).

EDIT: Correction, when I paste it into a browser, it asks for my login credentials and using the key/pass makes it work. I was asked by a Cloudant developer to pose this question to StackOverflow because they were unsure of what was happening (they also had my whole app with key/pass credentials and all).

¿Fue útil?

Solución

Ok, I seemed to have figured it out. I went down a few different paths after getting Will's answer yesterday, but alas most of them didn't even get me close. Some of them would change the error code from 500 to 401, but those were also changing the desired use of the BufferedInputStream. Regardless, if you want to use a BufferedInputStream, with a URLConnection, and include authentication for things such as Cloudant or whatever the case may be, you need to use setRequestProperty. Now, with that said, I tried this route a few different ways and adding the user and password in and doing a base64 switch after the connection has been made and the url was passed into the function didn't work at all. So, if you are able to pass the key/pass in with the URL as I originally had, the following will work perfectly for you... in theory (It is what worked for me anyhow).

        if (url.getUserInfo() != null) {
            String basicAuth = "Basic " + new String(Base64.encode(url.getUserInfo().getBytes(), 0));
            conn.setRequestProperty("Authorization", basicAuth);
        }

And of course, here it is implemented in my function so you can get the full effect.

public static String getURLStringResponse(URL url)
{
    String response = "";

    try {
        URLConnection conn = url.openConnection();

        // THIS WAS THE MAGIC ENTRY
        if (url.getUserInfo() != null) {
            String basicAuth = "Basic " + new String(Base64.encode(url.getUserInfo().getBytes(), 0));
            conn.setRequestProperty("Authorization", basicAuth);
        }
        // END OF MAGIC CHANGE

        BufferedInputStream bin = new BufferedInputStream(conn.getInputStream());

        byte[] contentBytes = new byte[1024];
        int bytesRead = 0;
        StringBuffer responseBuffer = new StringBuffer();

        while ((bytesRead = bin.read(contentBytes)) != -1)
        {
            response = new String(contentBytes, 0, bytesRead);
            responseBuffer.append(response);
        }

        return responseBuffer.toString();

    } catch (Exception e) {
        // TODO: handle exception
        Log.e(TAG, "getURLStringResponse Error as Follows");
        Log.e(TAG, e.toString());
    }

    return response;
}

I really hope this helps some of you out.

Otros consejos

My first thought is that you're probably not sending the credentials correctly in your request.

The https://<username>:<password>@username.cloudant.com URL syntax requires that you use an HTTP library which will parse that URL and generate the appropriate Basic Authentication header for the request. Given that you are using a low-level library, I'd guess you need to set this up manually.

You probably want to use the URL https://username.cloudant.com and set up the authentication headers as described in e.g. Connecting to remote URL which requires authentication using Java.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top