11package com .sakaimobile .development .sakaiclient20 .networking .utilities ;
22
3- import android .graphics .Bitmap ;
43import android .os .Build ;
54import android .webkit .CookieManager ;
65import android .webkit .WebResourceResponse ;
@@ -69,12 +68,12 @@ public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
6968 // We only need to intercept once authentication is complete, as the
7069 // the cookies do not change afterwards
7170 if (url .startsWith ("https://sakai.rutgers.edu/portal" ) && !gotHeaders )
72- return handleRequest (url );
71+ return handleRequest (view , url );
7372
7473 return super .shouldInterceptRequest (view , url );
7574 }
7675
77- private WebResourceResponse handleRequest (String url ) {
76+ private WebResourceResponse handleRequest (WebView view , String url ) {
7877 // After intercepting the request, we need to handle it
7978 // ourselves. This is done by creating an OkHttp3 Call,
8079 // which we add the Sakai cookies to. Without the cookies,
@@ -93,42 +92,53 @@ private WebResourceResponse handleRequest(String url) {
9392 return null ;
9493 }
9594
96- // After getting the response from Sakai, we can get the
97- // headers if it has what we want. Specifically, we need
98- // the X-Sakai-Session cookie.
99- String sakaiSessionHeader = response .headers ().get ("x-sakai-session" );
100- if (sakaiSessionHeader != null && !gotHeaders ) {
101- gotHeaders = true ;
102- sakaiLoadedListener .onLoginSuccess (username , password );
103- }
95+ // onPageStarted is not a good place to intercept credentials because
96+ // it is called too late in the release build variant. Instead, perform that
97+ // operation here. Call to post() is necessary for eval to run on the UI thread.
98+ view .post (() -> {
99+ // It is possible that we are moving away from this page,
100+ // so in case it is the login page, extract the username and password
101+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
102+ // We get both credentials as an array to prevent synchronization issues
103+ // with getting username and password separately.
104+ view .evaluateJavascript (
105+ "[document.querySelector('#username').value, document.querySelector('#password').value]" ,
106+ credentials -> {
107+ // JS-evaluated strings have double quotes surrounding them, necessitating
108+ // all the null and length checks, and odd substring indices.
109+ if (credentials != null && !"null" .equals (credentials )
110+ && !"undefined" .equals (credentials ) && credentials .length () > 7 ) {
111+ final String separator = "\" ,\" " ;
112+ int separatorIndex = credentials .indexOf (separator );
113+ this .username = credentials .substring (2 , separatorIndex );
114+ this .password = credentials .substring (separatorIndex + separator .length (), credentials .length () - 2 );
115+ }
116+ // Whether we got username and password or not, the authentication was
117+ // successful and this must be indicated to the LoginActivity
118+ tryCompleteLogin (response );
119+ }
120+ );
121+ } else {
122+ // Can't evaluate JS, just login as usual
123+ tryCompleteLogin (response );
124+ }
125+ });
104126
105127 // We need to return a WebResourceResponse, otherwise the
106128 // WebView will think that the request is hanging. The WebView
107129 // renders this response.
108130 // We do not need to modify the mimeType or encoding of the response.
109- return new WebResourceResponse (null , null ,
110- response .body ().byteStream ()
111- );
131+ return new WebResourceResponse (null , null , response .body ().byteStream ());
112132 }
113133
114- @ Override
115- public void onPageStarted (WebView view , String url , Bitmap favicon ) {
116- super .onPageStarted (view , url , favicon );
117- // It is possible that we are moving away from this page,
118- // so in case it is the login page, extract the username and password
119- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
120- // JS-evaluated strings have double quotes surrounding them, necessitating
121- // all the null and length checks
122- view .evaluateJavascript ("document.querySelector('#username').value" , username -> {
123- if (username != null && !"null" .equals (username ) && username .length () > 2 ) {
124- this .username = username .substring (1 , username .length () - 1 );
125- }
126- });
127- view .evaluateJavascript ("document.querySelector('#password').value" , password -> {
128- if (password != null && !"null" .equals (password ) && password .length () > 2 ) {
129- this .password = password .substring (1 , password .length () - 1 );
130- }
131- });
134+ private void tryCompleteLogin (Response response ) {
135+ // After getting the response from Sakai, we can get the
136+ // headers if it has what we want. Specifically, we need
137+ // the X-Sakai-Session cookie.
138+ String sakaiSessionHeader = response .headers ().get ("x-sakai-session" );
139+ if (sakaiSessionHeader != null && !gotHeaders ) {
140+ gotHeaders = true ;
141+ sakaiLoadedListener .onLoginSuccess (username , password );
132142 }
133143 }
134144}
0 commit comments