Tuesday, January 24, 2017

How to get the content of android apk file on mac

The easiest way to get the content of android apk file without any third party tools, is using unzip command line from mac's terminal app.

Just go to the folder containing the apk file from the terminal app, and run the below command
unzip androidapkfilename.apk -d targetfoldername

Android studio also has a menu item of "Build->Analyze APK...", but it cannot extract the content of APK file to local folder, so it is less useful.


Monday, January 16, 2017

Create WCF oData service on Window 10's IIS with Visual Studio 2015

To create odata service using WCF with Visual Studio 2015 on Windows 10 IIS, the first steps is following the instruction of 
https://www.codeproject.com/articles/1087982/create-a-wcf-dataservice-in-visual-studio
to create a oData WCF service. But the above link does not have a good description about how to publish the oData service to local IIS. 

To do that, following the instruction of 
https://debugmode.net/2014/06/18/publish-wcf-service-in-local-iis-on-visual-studio-2013/
The same steps also works on Visual Studio 2015.

Then for Windows 10 IIS, you will need to using the Turn Windows Feature on or off tool to turn on the below items:
1. Under Internet Information Services/World Wide Web Services\Application Development Features, turn on all items exception CGI
2.Undre .Net Framework 4.6 Advanced Services\WCF Services, turn on Http Activation item.

Then run the WCF project with the service url as below sample will get the service description
http://localhost/wcfodata/WcfDataService.svc/

Monday, January 9, 2017

How to set ios navigation view control animation direction to left/right or up/down

By default when using ios navigation view controller to manage several UIViewController using show segue, the default animation is left/right. However, in certain cases, it may make sense to change the animation direction to up/down.

It is possible to implement a custom UIStoryboardSegue to do so as mentioned in http://stackoverflow.com/questions/30763519/ios-segue-left-to-right

However, an easy way to change navigation animation direction from left/right to up/down is embedded each uiviewcontroller into its own UINavigationViewController, and you will get the up/down animation automatically. Note you will need to manually handle the back button on each UIViewController's navigation bar, so that it will unwind to the previous screen.

By the way, when using a single navigation view controller to manage several view controllers, you can only customize the navigation bar buttons title for the first view controller on storyboard, but not be able to do so for other view controllers. In order to customize the navigation bar items for other UIViewControllers from storyboard, you will need to drag and drop a navigation item (not navigation bar, as navigation bar is only for adding into UINavigationViewController) into the UIViewController from storyboard.

How cordova UIWebview passes javascript exec call into native code

When UIWebView is used, iosExec js method in cordova.js will be called by a cordova js bridge call.
The pokeNative method will be called to create an iframe element with the src of "gap://ready". When an iframe is added into the DOM tree, it will invoke the native side code in CDVUIWebViewDelegate.m method

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
Which will check whether the scheme is "gap", if so the native code will start to handle the bridge call.




Thursday, November 24, 2016

Understand oAuth authentication

Participants:
1. Resource owner, user who has credential to access the resource, like yourself.
2. Resource server, a web server where a resource is stored in there, like google drive.
3. Authorization server, an authentication server which manages the user identity, and authenticates the user, tokens issued by authorization server can be trusted by Resource server.
4. Client, application used by user (most likely resource owner) to work on the resource

Configuration:
clientID: "fb266000-544f-4db6-957e-1e05f530bb18",          
The client app must first register itself to authorization server to obtain a unique client id -- a unique string representing the registered client app. So when authorization request comes authorization server knows which client sends this request, Client id is not confidential.

grantType: "authorization_code"
Indicate the client type, confidential or public

authorizationEndpoint: "https://oauthasservices-wba2e8af2.int.sap.hana.ondemand.com/oauth2/api/v1/authorize",
Authorization endpoint - used by the client app to obtain authorization from the user. The user's identity will be authenticated at this url by user identity provider, the authentication method can be Basic, SAML, client cert, etc. This is the first step to establish oauth connection.

TokenEndpoint: "https://oauthasservices-wba2e8af2.int.sap.hana.ondemand.com/oauth2/api/v1/token",
Token endpoint - used by the client to exchange an authorization grant for an access token, typically with client id information.

redirectURL: "com.sap.fiori.client.debug://oauth2",
Redirection endpoint - used by the authorization server to return responses containing authorization credentials to the client via the resource owner user-agent.

OAuth authentication must be performed with TLS (https) connection, as token is passed in clear text.

Authentication process:
1. When client app needs to perform an oAuth authentication, it first needs to open a browser (or webview), and send the initial GET request to the authorization endpoint, the url also includes the parameter of
response_type (required)
client ID (required)
redirect url, 
scope
state
response_type should be set to "code" if grandType is set to "authorization_code". This request needs to be opened in a browser (or webView) user agent, so the web page can lead user to input his credential to finish the user authentication. The authorization server may validate the rediretURL with the value
registered in it to avoid sending the authorization code to malicious user agent.
Request sample:
https://server.sample.com/authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

2. When user authentication is succeeded, authorization server will send back 302 redirect request. The authorization code (or error) is included in the redirect url as "code" (or "error") parameter . The response url parameters include
code (required)
state (required)

example:
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
               &state=xyz

The webview or client app can parse this url parameter to get the authorization code string. If error happens, the url will include "error" parameter and "state" parameter.
example:
HTTP/1.1 302 Found
Location: https://client.example.com/cb?error=access_denied&state=xyz

The authorization code means user grands the client app to access the resource, and so as to use the authorization code to exchange the access token.

3. Once the authorization code is available, the webview opened in step 1 is no longer necessary. the client app can send the following request either in browser or in native library (js or native code). The second request is a POST request to tokenEndpoint, the post body includes
grandType (required)
client_ID (required)
code (required)
redirect url (required if redirect url is included in the initial authorization request)
scope

request sample:
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
4. Authorization server will return an access token and refresh token (in JSON string format) in the response.

Sample:
     {
       "access_token":"2YotnFZFEjr1zCsicMWpAA",
       "token_type":"example",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
       "example_parameter":"example_value"
     }
5. Client app can send the resource request to resource server by presenting the access token into request 's "Authorization" head as a "bearer". 
Header["Authorization"] = "Bearer " + access_token;
example:

   Authorization: Bearer mF_9.B5f-4.1JqM

The resource server must validate the access token and be sure the resource is covered by the scope.


6. In case the access token is expired, it can be refreshed by using refresh token. To do so, sending the request to token access endpoint, with the below parameter
grand_type (required, value must be "refresh_token")
refresh_token (required)
scope (optional)

example:
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA

The access token will be returned in the response.

Reference:
https://tools.ietf.org/html/rfc6749

Sunday, November 13, 2016

Swift helper methods for array, dictionary and set

Quite ofter it is needed to loop through each element in an array or dictionary to do certain operation on each element. Swift provides few helper methods to make the operation simple.

The array is used to demo these helper methods

1. sorted, sorted(by:)
Sort an array elements based on default or custom comparison logic. This is a swift standard library function.

Sample sort by default order
        let values = [2, -24, 5, 7]

        let sortedArray = values.sorted()
        print(sortedArray)
            //[-24, 2, 5, 7]

Sample sort by custom comparison

      let sortedArray2 = values.sorted (by: { (a: Int, b: Int) -> Bool in

              return a*a < b*b
            })
        print(sortedArray2)

       //[2, 5, 7, -24]

As the parameter is defined by sorted(by:) function, so the type information can be omitted as 
      let sortedArray3 = values.sorted (by: { a, b in
              return a*a < b*b
            })
        print(sortedArray3)

As the block body only as a single expression, the return clause can also be omitted
 let sortedArray4 = values.sorted (by: { a, b in
              a*a < b*b
            })
        print(sortedArray4)

In addition, the parameter can also referred by their index as $0, $1, $2 from the parameter list, so the expression can be simplified as 
 let sortedArray5 = values.sorted (by: 
              { $0*$0 < $1*$1
            })
        print(sortedArray5)

2. map
For each item in the array, do some operation on it and then return a new array with the processed items.
   let values = [2, -24, 5, 7]
   let square = values.map ({ (item:Int) -> Int in
         return item * item
        })

   print(square)   
   //output
   //[4, 576, 25, 49]

as the function only as a single unnamed block argument, the (), and parameter type information can be simplified as
       let square2 = values.map{ $0 * $0}

        print(square2) 


3. filter
Loop through the collection and return a new collection with the satisfied elements, the returned element type is same as original type

To get the positive even number from the above array
        let filtered = values.filter { (item: Int) -> Bool in
            let i = item % 2
            return i == 0 && item > 0
            }
            
        print((filtered))
        
Simplified express looks like below
        let filtered2 = values.filter {
            let i = $0 % 2
            return i == 0 && $0 > 0
            }
            
        print((filtered2))

4. reduce
reduce can be used to go through each element and accumulate the result from previous element, for example, to calculate the sum
The first parameter is the initial value, which does not need to be the same type as collection element 
        let output = values.reduce("the array is: ", {(ret, item: Int) in
            return ret + (String(item)) })
        print(output)
        //the array is: 2-2457

Simplified express is
        let output2 = values.reduce("the array is: "){$0 + (String($1)) }
        print(output2)

Thursday, November 10, 2016

ios WKWebView security behavior

1. if the main html page is loaded from https connection, then requests using http scheme will be blocked, including xhr request or script element. Even if creating a new iframe, setting the iframe's src to a http url will not load.  However, https content can be loaded into http html page.

2. when sending xhr request to server, the wkwebview didReceiveAuthenticationChallenge delegate method will only be called if the xhr request is sent to the same domain as the html DOM tree. If the xhr request is sent to a different domain, then didReceiveAuthenticationChallenge will not be called, and the 401 https status code will return the js code. This also applies to iframe. didReceiveAuthenticationChallenge delegate method will be called only if the xhr request sent to the same domain as iframe main url.

3. if the main html is loaded from a file url (file://somelocalfilepath), then any xhr requests using file url to the files of the same or sub local folder will fail due to cross domain limit. However, this error can be avoided by setting allowFileAccessFRomFileURLS property in wkwebview configuration as shown below
[theConfiguration.preferences setValue:@YES forKey:@"allowFileAccessFromFileURLs"];  

In addition, xhr request to any remote server will be handled same as cross domain xhr request, which requires the Access-Control-Allow-Origin header to be set, and will also not invoke didReceiveAuthenticationChallenge method.

However, if a iframe is created in the file url html page, then the iframe can be set to a remote url (http or https), and the xhr request inside the iframe on the same iframe domain will work and also can receive the didReceiveAuthenticationChallenge callback.