First post so forgive me if I forgot some rules :P
I'm developing an android app that requires basic to and fro data transfer to an app on google app engine. That app is written in python and I'm using the flask framework to communicate with the app (through http request, insecure and everything else I know but I just want a proof of concept more than anything else at the moment).
My problem is that when the python app is deployed on gae and I send a request from the android app on a physical device, I get no response, yet if I put the necessary URL in the browser, I get a response no problem.
Anyway the java function code:
private boolean doLogin(String username, String passwd){
boolean loggedIn = false;
try {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
URL url = new URL("http://appname.appspot.com/login/admin/admin");// + username + "/" + passwd+"/");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
//con.setRequestMethod("POST");
InputStream in = new BufferedInputStream(con.getInputStream());
if (readStream(in) == true){
loggedIn = true;
}
//con.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return loggedIn;
}
And the python function:
#webapp.route('/login/<username>/<passwd>', methods=["GET", "POST"])
def dologin(username, passwd):
def validusertype(u2check, p2check):
if db.GqlQuery("SELECT * FROM UserAccount WHERE username = :u2check AND password = :passwd", u2check = u2check, passwd=p2check).count() > 0:
qry = db.GqlQuery("SELECT * FROM UserAccount WHERE username = :u2check", u2check = u2check)
for r in qry.fetch(limit=None):
return r.usertype
return None
if request.method == "GET":
_type = validusertype(username, passwd)
if _type:
return "True"
# elif request.method == "POST":
# Fall though - if nothing above takes, we end up here.
return "False"
Any help at all would be greatly appreciated! Also the python code works, Copy/paste operation kinda screwed it up a bit.
Update
I figured out what was wrong. I was missing a few essential pieces to the puzzle but thanks to #hgoebl for pointing out my errors (ie the whole function, ow my ego). So here's the replacement code for anyone that may need it.
try {
URL url = new URL("http://app.appspot.com/login/admin/admin");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readStream(is);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
Also to put these few lines in onCreate():
StrictMode.ThreadPolicy policy = new StrictMode.
ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Related
I am having trouble developing a Android app that I want to authenticate with Django REST framework to securely access information. I am being successfully issued a REST token but IsAuthenticated remains false for all of my views.
In Django, I have a class based view that responds if both authentication.TokenAuthentication permissions.IsAuthenticated are valid:
class TestAuthView(APIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
def get(self, request, format=None):
return GetRestData()
In Android, I acquire a token by POSTing my uname and passwd to the default url: /rest-auth/login/which responds with token: {"key":"c03c1238ab99d91301d34567bda9d417d2b48c0c"}
public static String getResponseFromHttpUrl(String... params) throws IOException {
ArrayList<AbstractMap.SimpleEntry<String,String>> paramssss = new ArrayList<>();
paramssss.add(new AbstractMap.SimpleEntry<>("username", "root"));
paramssss.add(new AbstractMap.SimpleEntry<>("password", "mypass"));
URL url = new URL(params[0]);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(3000);
urlConnection.setConnectTimeout(3000);
urlConnection.setRequestMethod("POST");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
OutputStream os = urlConnection.getOutputStream();
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(os, "UTF-8"));
writer.write(getQuery(paramssss));
writer.flush();
writer.close();
os.close();
urlConnection.connect();
try {
InputStream in = urlConnection.getInputStream();
Scanner scanner = new Scanner(in);
scanner.useDelimiter("\\A");
boolean hasInput = scanner.hasNext();
if (hasInput) {
return scanner.next(); //eg. {"key":"c03c1238ab99d91301d34567bda9d417d2b48c0c"}
} else {
return null;
}
} finally {
urlConnection.disconnect();
}
}
I then store the token and later use it to request some data:
#Override
protected String doInBackground(String... sUrl) {
try {
URL url = new URL(sUrl[0]);
URLConnection urlConnection = url.openConnection();
String authToken = "c03c1238ab99d91301d34567bda9d417d2b48c0c"; //just use a constant string for now..
urlConnection.addRequestProperty("Authorization", "Token " + authToken);
urlConnection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
urlConnection.connect();
...
process the response
...
Looking at the Django logs I see that login succeeds but the GET request fails with HTTP_401_UNAUTHORIZED:
[08/Oct/2019 22:18:53] "POST /rest-auth/login/ HTTP/1.1" 200 50
[08/Oct/2019 22:18:53] "GET /update/ HTTP/1.1" 401 58
When I change the permission_classes to AllowAny:
class TestAuthView(APIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.AllowAny,) //Changed this!!!
def get(self, request, format=None):
return GetRestData()
The response contains the expected REST data and everything succeeds:
[08/Oct/2019 22:24:57] "POST /rest-auth/login/ HTTP/1.1" 200 50
[08/Oct/2019 22:25:02] "GET /update/ HTTP/1.1" 200 19451876
I don't understand how I should properly authenticate my Android app so that IsAuthenticated will not always be False?
Currently I submit a username and password to /rest-auth/login/ and am issued a rest token. But must I also login somewhere else to get a CSRF token and use that as well?
I am not familiar with the need for permissions.IsAuthenticated and if it is even valid for Android apps? I mean do I just leave the permission as AllowAny for non-browser Android apps? I feel it's a bad idea..?
I've been plugging at this for a few days and would kindly appreciate any help!
I need to reliably detect if a device has full internet access, i.e. that the user is not confined to a captive portal (also called walled garden), i.e. a limited subnet which forces users to submit their credentials on a form in order to get full access.
My app is automating the authentication process, and therefore it is important to know that full internet access is not available before starting the logon activity.
The question is not about how to check that the network interface is up and in a connected state. It is about making sure the device has unrestricted internet access as opposed to a sandboxed intranet segment.
All the approaches I have tried so far are failing, because connecting to any well-known host would not throw an exception but return a valid HTTP 200 response code because all requests are routed to the login page.
Here are all the approaches I tried but they all return true instead of false for the reasons explained above:
1:
InetAddress.getByName(host).isReachable(TIMEOUT_IN_MILLISECONDS);
isConnected = true; <exception not thrown>
2:
Socket socket = new Socket();
SocketAddress sockaddr = new InetSocketAddress(InetAddress.getByName(host), 80);
socket.connect(sockaddr, pingTimeout);
isConnected = socket.isConnected();
3:
URL url = new URL(hostUrl));
URLConnection urlConn = url.openConnection();
HttpURLConnection httpConn = (HttpURLConnection) urlConn;
httpConn.setAllowUserInteraction(false);
httpConn.setRequestMethod("GET");
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;
So, how do I make sure I connected to an actual host instead of the login redirection page? Obviously, I could check the actual response body from the 'ping' host I use but it does not look like a proper solution.
For reference, here is the 'official' method from the Android 4.0.1 AOSP code base:
WifiWatchdogStateMachine.isWalledGardenConnection(). I am including the code below just in case the link breaks in the future.
private static final String mWalledGardenUrl = "http://clients3.google.com/generate_204";
private static final int WALLED_GARDEN_SOCKET_TIMEOUT_MS = 10000;
private boolean isWalledGardenConnection() {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(mWalledGardenUrl); // "http://clients3.google.com/generate_204"
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
// We got a valid response, but not from the real google
return urlConnection.getResponseCode() != 204;
} catch (IOException e) {
if (DBG) {
log("Walled garden check - probably not a portal: exception "
+ e);
}
return false;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
This approach relies on a specific URL, mWalledGardenUrl = "http://clients3.google.com/generate_204" always returning a 204 response code. This will work even if DNS has been interfered with since in that case a 200 code will be returned instead of the expected 204. I have seen some captive portals spoofing requests to this specific URL in order to prevent the Internet not accessible message on Android devices.
Google has a variation of this theme: fetching http://www.google.com/blank.html will return a 200 code with a zero-length response body. So if you get a non-empty body this would be another way to figure out that you are behind a walled garden.
Apple has its own URLs for detecting captive portals: when network is up IOS and MacOS devices would connect to an URL like http://www.apple.com/library/test/success.html, http://attwifi.apple.com/library/test/success.html, or http://captive.apple.com/hotspot-detect.html which must return an HTTP status code of 200 and a body containing Success.
NOTE:
This approach will not work in areas with regionally restricted Internet access such as China where the whole country is a walled garden, and where some Google/Apple services might be blocked. Some of these might not be blocked: http://www.google.cn/generate_204, http://g.cn/generate_204, http://gstatic.com/generate_204 or http://connectivitycheck.gstatic.com/generate_204 — yet these all belong to google so not guaranteed to work.
Another possible solution might be to connect via HTTPS and inspect the target certificate. Not sure if walled gardens actually serve the login page via HTTPS or just drop the connections. In either case, you should be able to see that your destination is not the one you expected.
Of course, you also have the overhead of TLS and certificate checks. Such is the price of authenticated connections, unfortunately.
I believe preventing redirection for your connection will work.
URL url = new URL(hostUrl));
HttpURLConnection httpConn = (HttpURLConnection)url.openConnection();
/* This line prevents redirects */
httpConn.setInstanceFollowRedirects( false );
httpConn.setAllowUserInteraction( false );
httpConn.setRequestMethod( "GET" );
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;
If that doesn't work, then I think the only way to do it is to check the body of the response.
This has been implemented on Android 4.2.2+ version - I find their approach fast and interesting :
CaptivePortalTracker.java detects walled garden as follows
- Try to connect to www.google.com/generate_204
- Check that the HTTP response is 204
If the check fails, we are in a walled garden.
private boolean isCaptivePortal(InetAddress server) {
HttpURLConnection urlConnection = null;
if (!mIsCaptivePortalCheckEnabled) return false;
mUrl = "http://" + server.getHostAddress() + "/generate_204";
if (DBG) log("Checking " + mUrl);
try {
URL url = new URL(mUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
// we got a valid response, but not from the real google
return urlConnection.getResponseCode() != 204;
} catch (IOException e) {
if (DBG) log("Probably not a portal: exception " + e);
return false;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
if you are already using retrofit you can do it by retrofit. just make a ping.html page and send an head request to it using retrofit and make sure your http client is configured like below: (followRedirects(false) part is the most important part)
private OkHttpClient getCheckInternetOkHttpClient() {
return new OkHttpClient.Builder()
.readTimeout(2L, TimeUnit.SECONDS)
.connectTimeout(2L, TimeUnit.SECONDS)
.followRedirects(false)
.build();
}
then build your retrofit like below:
private InternetCheckApi getCheckInternetRetrofitApi() {
return (new Retrofit.Builder())
.baseUrl("[base url of your ping.html page]")
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.client(getCheckInternetOkHttpClient())
.build().create(InternetCheckApi.class);
}
your InternetCheckApi.class would be like:
public interface InternetCheckApi {
#Headers({"Content-Typel: application/json"})
#HEAD("ping.html")
Call<Void> checkInternetConnectivity();
}
then you can use it like below:
getCheckInternetOkHttpClient().checkInternetConnectivity().enqueue(new Callback<Void>() {
public void onResponse(Call<Void> call, Response<Void> response) {
if(response.code() == 200) {
//internet is available
} else {
//internet is not available
}
}
public void onFailure(Call<Void> call, Throwable t) {
//internet is not available
}
}
);
note that your internet check http client must be separate from your main http client.
This is best done here as in AOSP :
https://github.com/aosp-mirror/platform_frameworks_base/blob/6bebb8418ceecf44d2af40033870f3aabacfe36e/core/java/android/net/captiveportal/CaptivePortalProbeResult.java#L61
https://github.com/aosp-mirror/platform_frameworks_base/blob/e3a0f42e8e8678f6d90ddf104d485858fbb2e35b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
private static final String GOOGLE_PING_URL = "http://google.com/generate_204";
private static final int SOCKET_TIMEOUT_MS = 10000;
public boolean isCaptivePortal () {
try {
URL url = new URL(GOOGLE_PING_URL);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
return (urlConnection.getResponseCode() != 204)
&& (urlConnection.getResponseCode() >= 200)
&& (urlConnection.getResponseCode() <= 399);
} catch (Exception e) {
// for any exception throw an exception saying check was unsuccesful
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
Please note this will probably not work on a proxy network and something more advanced as in the AOSP url needs to be done
I have a wamp server. I have written my android client. If I run that app, the response is fine on an emulator...but the same code does not work on the real device, I mean I dont get a response.....
Here s the code...
public static final String SERVER_URL = "http://192.168.1.3/AndroidListServer/server.php?command=getAnimalList";
private static String executeHttpRequest(String data) {
String result = "";
try {
URL url = new URL(SERVER_URL);
URLConnection connection = url.openConnection();
/*
* We need to make sure we specify that we want to provide input and
* get output from this connection. We also want to disable caching,
* so that we get the most up-to-date result. And, we need to
* specify the correct content type for our data.
*/
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// Send the POST data
DataOutputStream dataOut = new DataOutputStream(connection.getOutputStream());
dataOut.writeBytes(data);
dataOut.flush();
dataOut.close();
// get the response from the server and store it in result
DataInputStream dataIn = new DataInputStream(connection.getInputStream());
String inputLine;
while ((inputLine = dataIn.readLine()) != null) {
result += inputLine;
}
dataIn.close();
} catch (IOException e) {
/*
* In case of an error, we're going to return a null String. This
* can be changed to a specific error message format if the client
* wants to do some error handling. For our simple app, we're just
* going to use the null to communicate a general error in
* retrieving the data.
*/
e.printStackTrace();
result = null;
}
return result;
}
Solved it guys....it was the problem with the firewall as Rajesh mentioned...I should do a thorough testing of all the possible parameters....but hey I'm learning :)
My Android tablet application does not work with ICS due to a Login problem. When I looked at my code and ran it under debug mode on an ICS tablet, I see the problem but I don't understand it. The code functions correctly on all Honeycomb models that i have tested and in fact I have two tablets hooked up to my computer (one Samsung Galaxy Tab running 3.2, and a Motorola Xoom wifi running 4.0.3) and the code fails on ICS and works on HC.
The failure is a Socket Timeout exception. The timeout was 2000ms, but I upped it to 100000ms to test and it had no impact.
Using the browser on the ICS tablet, I can go to the URL and it responds, so it doesn't appear to be network related.
I am running on a background thread using AsyncTask.
Slurp just takes all of the input from the InputStream and using StringBuilder creates a string representation. Its not actually useful in this request but I added it to see what the server was replying with.
I am POSTing to the page the same way a user authenticates using the form, which is why I am using x-www-form-urlencoded.
Again, this code functions perfectly on Honeycomb but fails on ICS.
The code makes a connection but fails when it asks for a response from the server, almost like the server is still waiting for something... anyway, here is the code:
static public String authenticate(String service_url, String username, String password) throws IOException {
if (username == null || password == null)
throw new IOException();
String charset = "UTF-8";
String query = String.format("Email=%s&Password=%s",URLEncoder.encode(username, charset),URLEncoder.encode(password, charset));
byte [] data = query.getBytes(charset);
URL url = new URL(service_url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Length", Integer.toString(data.length));
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setReadTimeout(5000); // 2 second timeout.
try {
connection.connect();
DataOutputStream pw = new DataOutputStream (connection.getOutputStream());
pw.writeBytes(query);
pw.flush();
pw.close();
int code = connection.getResponseCode(); //SOCKET TIMEOUT HERE
if (code == 200 || code == 302)
{
InputStream is = connection.getInputStream();
String value = slurp(is);
List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
if (cookies == null)
throw new IOException();
for (String cookie : cookies) {
if (cookie.startsWith("cpms")) {
cookieTime = new DateTime(); //crazy but the expires time in the cookie is not actually accurate.
return cookie; // this is the only correct path out.
}
}
}
else
Logger.e(StaticUtils.class, "Invalid response code while logging in: " + code);
}
catch (IOException ioe)
{
Logger.e(StaticUtils.class, ioe);
throw ioe; // log it and then throw it back.
} finally {
connection.disconnect();
}
return null;
}
I need to find operator name from phone no. using this website in my android application.
Requesting and parsing HTML in the application works fine.
When I query request string from the app:
address: .https://nummertjanster.pts.se/net/en/Nummerkapacitet/Enskiltnummer?&_rp/pts.SearchNumber_ndc=70&_rp/pts.SearchNumber_operator=Tele2+Sverige+AB&_rp/pts.SearchNumber_telnumber=4264128
I need to specify 'operator name' ..which is wierd I guess.
The problem is no matter which number (ndc-telnumber) I enter if I specify a operator name in the request string the resulting webpage shows that operator name.
Here are some numbers to test:
073-3355433 = Telenor Sverige AB
073-6107353 = Tele 2 Sverige AB
070-3999266 = TeliaSonera Sverige AB
073-2404070 = Glocalnet AB
How can I find the proper operator name for a specific number?
Thanks for your any help.
There are a couple of issues with the site that prevent things from working:
It needs a cookie for the POST to work.
The operator name is actually not returned as part of the page, it is returned as part of a location redirect (302).
This chunk of code does what you want (I was dodging work, so I actually tidied it up for you): it hits the main page, fetches/extracts the cookie returned, posts the area code and number to the website and then intercepts the Location header and pulls out the operator name. Note that the area code is not always 3-digits (so for example for 073-3355433 you would do String operatorName = findOperator("73","3355433");.
String findOperator(String ndc, String number)
{
String parameters = "action=search&ndc="+ndc+"&number="+number+"&search=S%F6k";
HttpURLConnection httpUrlConnection = null;
OutputStream outputStream = null;
InputStream inputStream = null;
int code = 0;
String response = null;
try {
java.net.URI u = new java.net.URI("https://nummertjanster.pts.se/net/sv/Nummerkapacitet/Enskiltnummer");
httpUrlConnection = (HttpURLConnection) u.toURL().openConnection();
httpUrlConnection.setConnectTimeout(7500);
httpUrlConnection.setReadTimeout(7500);
httpUrlConnection.setRequestMethod("GET");
httpUrlConnection.connect();
String cookie = httpUrlConnection.getHeaderField("Set-Cookie");
u = new java.net.URI("https://nummertjanster.pts.se/actionrequest/sv/Nummerkapacitet/Enskiltnummer?__ac_/pts.SearchNumber");
httpUrlConnection = (HttpURLConnection) u.toURL().openConnection();
httpUrlConnection.setConnectTimeout(7500);
httpUrlConnection.setReadTimeout(7500);
httpUrlConnection.setRequestProperty("Cookie", cookie);
httpUrlConnection.setRequestMethod("POST");
httpUrlConnection.setDoOutput(true);
httpUrlConnection.connect();
outputStream = httpUrlConnection.getOutputStream();
outputStream.write(parameters.getBytes("UTF-8"));
httpUrlConnection.setInstanceFollowRedirects(false);
try {
inputStream = httpUrlConnection.getInputStream();
} catch (IOException e) {
//andrologger.warn("An error occurred while POSTing to " + url, e);
}
code = httpUrlConnection.getResponseCode();
response = httpUrlConnection.getHeaderField("Location");
if(response != null){
response = response.split("&")[2].split("=")[1];
}
}catch(Exception e1){
android.util.Log.v("Configuration","Exception: "+e1.getMessage(), e1);
} finally {
closeQuietly(outputStream);
closeQuietly(httpUrlConnection);
}
return response;
}
Tested it on my phone and it works fine: let me know how it works for you.
It clearly doesn't work restfully. You'll have to find another way.
Perhaps imitate the form post that the page is doing: http://www.androidsnippets.com/executing-a-http-post-request-with-httpclient
EDIT this doesn't work
Pretty simple:
https://nummertjanster.pts.se/net/sv/Nummerkapacitet/Enskiltnummer?&__rp_/pts.SearchNumber_ndc=PUT_AREA_NUMBER_HERE&__rp_/pts.SearchNumber_operator=xxno_operatorxx&__rp_/pts.SearchNumber_telnumber=PUT_NUMBER_HERE
i.e.
https://nummertjanster.pts.se/net/sv/Nummerkapacitet/Enskiltnummer?&__rp_/pts.SearchNumber_ndc=696&__rp_/pts.SearchNumber_operator=xxno_operatorxx&__rp_/pts.SearchNumber_telnumber=1788300