SMP Kapsel Development PDF
SMP Kapsel Development PDF
SMP Kapsel Development PDF
Contents
Kapsel Development .............................................................1
Developing Kapsel Applications ......................................3
Setting Up the Development Environment ......................3
Configuring the Application in the Management
Cockpit .......................................................................7
Defining Applications .............................................7
Defining Back-end Connections for Native and
Hybrid Apps .......................................................9
Defining Application Authentication .....................11
Creating an Apache Cordova Project ...........................13
Project Settings ....................................................15
Kapsel Plugins ..............................................................16
Using the Kapsel AppUpdate Plugin ....................17
Using the Logon Plugin ........................................42
Using the Kapsel AuthProxy Plugin ...................121
Using the Logger Plugin ....................................178
Using the Kapsel Push Plugin ...........................211
Using the Kapsel EncryptedStorage Plugin .......246
Using the Kapsel Settings Plugin .......................274
Developing a Kapsel Application With OData Online . 288
Creating an OData Application ..........................288
Creating an Application Connection ..................289
Getting Application Settings ...............................292
Running and Testing Kapsel Applications ...................294
Client-side Debugging .......................................294
Running the Kapsel Application on Android ......295
Running the Kapsel Application on iOS .............295
Package and Deploy Kapsel Applications ..................295
Generating and Uploading Kapsel App Files
Using the Command Line Interface ...............295
Kapsel Development
iii
Contents
iv
Kapsel Development
Kapsel Development
Kapsel is a set of SAP plugins for Apache Cordova.
Apache Cordova provides a suite of APIs you can use to access native capabilities. The
Cordova container provides JavaScript libraries that give you consistent APIs you can call the
same way on any supported device. Beginning with Apache Cordova 3.0, the Cordova
container is simply a holder in which any APIs and extensions are implemented as plugins.
Apache Cordova includes a command line interface for managing Cordova applications and
the application development process.
Kapsel Development
Kapsel Development
Kapsel leverages the Cordova application container and provides SAP plugins to make the
Cordova container enterprise-grade, allowing it to more seamlessly integrate with SAP
Mobile Platform Server. The Kapsel plugins provide capabilities like application life cycle
management, implementation of a common logon manager and single sign-on (SSO),
integration with SAP Mobile Platform Server-based push notifications and so on. Since
Kapsel Development
Kapsel is implemented without modifying the Cordova container, it is compatible with
anything else you develop with Cordova.
Verify that you can access SAP Mobile Platform Server from your machine
If you are using Windows, download and extract Apache Ant and add it to the system
variable path, PATH=%PATH%;C:\apache-ant-<version>\bin. See http://
ant.apache.org.
See http://service.sap.com/pam to verify that you are using the supported versions for the
Kapsel development environment.
Kapsel Development
Kapsel Development
Android Requirements
Android tools run on Windows, Linux, and OS X. To build Kapsel apps for Android, you
need:
technetwork/java/javase/downloads/index.html
downloads/
Start the Eclipse environment.
From the Help menu, select Install New Software.
Click Add.
In the Add Repository dialog, enter a name for the new plugin.
Enter one of the following for URL:
https://dl-ssl.google.com/android/eclipse/
http://dl-ssl.google.com/android/eclipse/
6. Click OK.
7. Select Developer Tools and click Next.
8. Review the tools to be downloaded.
9. Click Next.
10. Read and accept the license agreement and click Finish.
11. Once the installation is complete, restart Eclipse.
1.
2.
3.
4.
5.
Kapsel Development
If you prefer to work in an IDE other than Eclipse, you do not need to install Eclipse or ADT.
You can simply use the Android SDK tools to build and debug your application.
Installing the Google USB Driver
The Google USB Driver for Windows is as an optional SDK component you need only if you
are developing on Windows and want to connect a Google Android-powered device (such as a
Nexus 7) to your development environment over USB.
Download the Google USB driver package from http://developer.android.com/sdk/winusb.html.
Installing the Android SDK
Install the Android SDK for plugin use with your IDE.
1. Confirm that your system meets the requirements at http://developer.android.com/sdk/
requirements.html.
2. Download and install the supported version of the Android SDK starter package.
3. Add the Android SDK to your PATH environment variable:
On Windows, add <Android SDK Location>\tools to the PATH environment
variable
On OS X, the command is: export PATH=$PATH:<path to Android SDK>/
tools
4. Launch the Android SDK Manager and install the Android tools (SDK Tools and SDK
Platform-tools) and the Android API.
5. Launch the Android Virtual Device Manager, and create an Android virtual device to
use as your emulator.
Note: (For offline applications only) Due to limitation on the emulator, you cannot
determine the network connection state. For more information on other limitations, see
Emulator Limitations in http://developer.android.com/tools/devices/
emulator.html#limitations at the Android Developer Web site.
iOS Requirements
To build Kapsel apps for iOS, you need:
Mac OS X
Xcode and Xcode command line tools
For testing on iOS devices (not the simulator), you need:
An Apple Developer account
iOS development certificate
Provisioning files for each device you are testing with
Kapsel Development
Kapsel Development
Installing Node.js
Use Node.js v0.10.11 and later, and its package manager, npm, to install Apache Cordova. See
http://nodejs.org/. You can see the version installed by using the node command: node v.
You must add the Node.js folder to your system PATH.
Note: If you are using a proxy server you must configure npm. At the command prompt,
enter:
npm config set proxy http://proxy_host:port
npm config set https-proxy http://proxy_host:port
Kapsel Development
sudo chown -R user_name /usr/local/lib/node_modules/cordova
You can copy the command text from the error message and paste it in at the command
prompt at the bottom of the terminal window.
2. On Mac, when prompted, enter your root user password.
3. Verify the Cordova installation by entering this command at the command prompt, or in
the terminal window:cordova v
The output shows the Cordova version installed, for example, 3.0.9.
You should also scroll back through the entire installation history shown in the terminal
and look for errors to verify the installation was successful.
Installing ios-sim
To allow the Cordova command line to start the iOS simulator on Mac, you must install iossim.
1.
2.
3.
4.
Task
Defining Applications
Create a new native, hybrid, or Agentry application definition. The definition enables you to
manage the application using Management Cockpit.
1. In Management Cockpit, select Applications, and click New.
2. In the New Application window, enter:
Kapsel Development
Kapsel Development
Field
Value
ID
Name
Vendor
Type
Application type.
Native native iOS and Android applications.
Hybrid container-based applications, such as Kapsel.
Agentry metadata-driven application.
Note: You can configure only one Agentry application per SAP
Mobile Platform Server. Once configured, Agentry no longer
appears as an option.
Kapsel Development
Field
Value
Description
3. Click Save. Application-related tabs appear, such as Back End, Authentication, Push, and
so forth. You are ready to configure the application, based on the application type.
Note: These tabs appear in Management Cockpit only after you define or select an
application. The tabs used differ by application type.
Value
The back-end connection URL, or service document URL the application uses to
access business data on the back-end system or service. The service document URL
is the document destination you assigned to the service in Gateway Management
Cockpit. Typical format:
http://host:port/gateway/odata/namespace/Connection_or_ServiceName...
Examples:
http://testapp:65908/help/abc/app1/opg/sdata/TESTFLIGHT
http://srvc3333.xyz.com:30003/sap/opu/odata/RMTSAMPLE
Kapsel Development
Kapsel Development
Field
Value
(Optional) Whether to use system proxy settings in the SAP Mobile Platform
props.ini file to access the back-end system. This setting is typically disabled,
because most back-end systems can be accessed on the intranet without a proxy. The
setting should only be enabled in unusual cases, where proxy settings are needed to
access a remote back-end system outside of the network. When enabled, this particular connection is routed via the settings in props.ini file.
Rewrite
URL
(Optional) Whether to mask the back-end URL with the equivalent SAP Mobile
Platform Server URL. This is necessary to ensure the client makes all requests via
SAP Mobile Platform Server and directly to the back end. Rewriting the URL also
ensures that client applications need not do any additional steps to make requests to
the back end via SAP Mobile Platform Server. If enabled, the back-end URL is
rewritten with the SAP Mobile Platform Server URL. By default, the property is
enabled.
Allow
anonymous connections
(Optional) Whether to enable anonymous access. This means the application user
can access the application without entering a user name and password. However, the
back-end system still requires log on credentials to access the data, whether it is a
read-only user, or a back-end user with specific roles. If enabled, enter the log on
credential values used to access the back-end system:
User name supply the user name for the back-end system.
Password supply the password for the back-end system.
If disabled, you do not need to provide these credentials. By default, the property is
disabled.
Note: If you use Allow Anonymous Connections for a native OData application, do
not assign the No Authentication Challenge security profile to the application, or the
anonymous OData requests will not be sent (Status code: 401 is reported).
Maximum
Connections
The number of back-end connections that are available for connection pooling for
this application. The larger the pool, the larger the number of possible parallel
connections to this specific connection. The default is 500 connections. Factors to
consider when resetting this property:
The expected number of concurrent users of the application.
The load acceptable to the back-end system.
The load that the underlying hardware and network can handle.
Note: The maximum connections can be increased only if SAP Mobile Platform
Server hardware can support the additional parallel connections, and if the underlying hardware and network infrastructure can handle it.
10
Kapsel Development
Field
Value
Certificate The name under which the administrator has imported the certificate key-pair in the
Alias
smp_keystore file. The alias must be set when the back-end URL is
https://, and the back-end server requires mutual authentication. There are
conditions when https is used but the server does not require a client certificate. This
certificate alias is required when the back end requires mutual SSL connectivity. Use
the alias of a certificate stored in the SAP Mobile Platform Server keystore. SAP
recommends that the CN value of the generated certificate be the fully qualified
domain name of SAP Mobile Platform Server.
Kapsel Development
11
Kapsel Development
Task
Security profiles are comprised of one or more authentication providers. These authentication
providers can be shared across multiple security profiles, and can be modified in Management
Cockpit. You can stack multiple providers to take advantage of features in the order you chose;
the Control Flag must be set for each enabled security provider in the stack.
1. From Management Cockpit, select Applications > Authentication.
2. Click Existing Profile.
Note: You can also create a new profile.
3. In Name, select a security profile name from the list. The name appears under Security
Profile Properties, and one or more security providers appear under Authentication
Providers.
4. Under Security Profile Properties, enter values.
Field
Value
Name
Check Impersonation
(Optional) In token-based authentication, whether to allow authentication to succeed when the user name presented cannot be
matched against any of the user names validated in the login
modules. To prevent the user authentication from succeeding in
this scenario, the property is enabled by default.
5. Under Authentication Providers, you can select a security profile URL to view its settings.
To change its settings, you must modify it through Settings > Security Profiles.
Kapsel Security Matrix
Use one of the supported security configurations to secure your applications.
12
Security Configuration
Implemented Using
Security Provider
No Authentication
Challenge
No Authentication
Challenge
HTTP/HTTPS
Authentication
Kapsel Development
Security Configuration
Implemented Using
Security Provider
HTTP/HTTPS
Authentication
Encrypted storage
Data Vault
Any
Kapsel Development
13
Kapsel Development
This may take a few minutes to complete, as an initial download of the template project that
is used is downloaded to C:\Users\user\.cordova on Windows, or ~/users/
user/.cordova on Mac.
The parameters are:
(Required) <Project_Folder> the directory to generate for the project.
(Optional) <Application_ID> must match the Application ID as configured on SAP
Mobile Platform Server for the application, which is reverse-domain style, for
example, com.sap.kapsel.
Note: <Application_ID> cannot be too simple. For example, you can have "a.b" for an
ID, but you cannot have "MyApplicationId." The ID is used as the package name
(name space) for the application and it must be at least two pieces separated by a
period, otherwise, you will get build errors.
(Optional) <Application_Name> name for the application.
Note: Android is supported on both Windows and OS X, but iOS is supported only on OS
X.
Note: You must add the platform before you add any Kapsel plugins.
The project directory structure is similar to this:
LogonDemo/
|--.cordova/
|-- merges/
| |-- android/
| `-- ios/
|-- platforms/
| |-- android/
14
Kapsel Development
| `-- ios/
|-- plugins/
|-- www/
-- config.xml
`
.cordova identifies the project as a Cordova project. The command line interface
uses this folder for storing its lazy loaded files. The folder is located immediately under
your users home folder (On Windows, c:\users\user_name\ , and on
Macintosh, /users/user_name/.cordova).
merges contains your Web application assets, such as HTML, CSS, and JavaScript
files within platform-specific subfolders. Files in this folder override matching files in
the www/ folder for each respective platform.
www this folder contains the main HTML, CSS, and JavaScript assets for your
application. The config.xml file contains meta data and native application
information needed to generate the application. The index.html file is the default
page of the application. Once you finish editing your project's files, update the platform
specific files using the cordova -d -prepare command.
platforms native application project structures are contained in subfolders for the
platforms you added to your application.
5. (Optional) You can test your Cordova project by opening it in the respective development
environment, for example, Xcode or Eclipse with the ADT plugins, and running it on the
simulator or emulator.
6. Add the plugins. For example, to add the Cordova console plugin and the Kapsel Logon
plugin on Windows, enter:
Note: The path you enter to the Kapsel plugin must be the absolute path (not relative
path).
7. Edit the Web application content in the project's www folder and use the cordova prepare
command to copy that content into the Android and iOS project folders:
cordova -d prepare android
cordova -d prepare ios
Project Settings
To set application configuration parameters, use the Cordova platform-independent
config.xml file.
To modify application metadata, edit the config.xml file. The config.xml file is
located in the www directory in your project. For information about the project settings for each
platform, see http://cordova.apache.org/docs/en/3.0.0/
config_ref_index.md.html#Configuration%20Reference.
Kapsel Development
15
Kapsel Development
Kapsel Plugins
Developers use one or more Kapsel plugins in Cordova applications to add SAP Mobile
Platform awareness and capabilities to the application. The plugins that you use vary
depending on your applications requirements. As they are standard Cordova plugins, manage
Kapsel plugins in a Cordova project using the standard Cordova CLI plugin commands.
Kapsel Plugin
Use
AppUpdate
Logon
Manages user onboarding and the authentication process for SAP Mobile
Platform applications. Most other Kapsel plugins use capabilities that
this plugin exposes. The plugin interfaces with the SAP Afaria client as
well as the Client Hub application to help manage authentication and
single sign-on.
You can install this plugin standalone, or it is automatically installed with
AppUpdate.
16
AuthProxy
Logger
Lets you have an application write entries to a local log, which can be
uploaded to the SAP Mobile Platform Server for analysis. The SAP
Mobile Platform administrator can manage setting the application log
remotely from the server and upload device logs to the server without user
intervention.
Push
Kapsel Development
Kapsel Plugin
Use
EncryptedStorage
Adds an encrypted persistent store (key/value pair) to a Cordova application, which allows you to build an application that securely stores
application data while offline, or while the application is not running.
Unlike the built-in local storage, EncryptedStorage is nonblocking.
Settings
Required if you are using the Push plugin. Manages the exchange of
settings information between the Kapsel app and the SAP Mobile Platform server. Used by the Push plugin.
config.xml File
Example Value
Revision
hybridapprevision
This shows an example of app-specific settings configuration for a sample app in Management
Cockpit.
Kapsel Development
17
Kapsel Development
Kapsel Development
download takes place. The www folder contents in the Kapsel application bundle are then
read, as if from a downloaded revision. Future revisions to the www folder contents
uploaded to the SAP Mobile Platform Server are downloaded normally by the AppUpdate
plugin. See Managing Update Versions and Revisions.
4. Once an update is downloaded by the AppUpdate plugin, there are a series of configurable
behaviors for handling the end-user experience, and for when the update is applied.
The default behavior is to display a modal alert to the user with options to accept or defer
updates. If the end user accepts the update, the Web application session is restarted within
the Kapsel application container, and the new version is loaded.
Example 1: User Accepts App Update
1. The AppUpdate function starts and triggers any required log on process.
2. Checking event is fired by AppUpdate.
3. AppUpdate finds that an update is available on the server, and the downloading event
fires.
4. Updates finish downloading.
5. The sap.AppUpdate.onupdateready function is triggered.
6. A prompt asks the user to reload the application.
7. The user accepts the prompt.
8. The sap.AppUpdate.reloadApp function is called and the updated application
loads.
Example 2: User Defers Update Action
1. The AppUpdate function starts and triggers any required log on process.
2. Checking event is fired by AppUpdate.
3. AppUpdate finds that an update is available on the server, and the downloading event is
fired.
4. Updates finish downloading.
5. The sap.AppUpdate.onupdateready function is triggered.
6. A prompt asks the user to reload the application.
7. The user cancels the prompt.
8. The sap.AppUpdate.onupdateready function is triggered the next time the
application is resumed or started.
Configuring the AppUpdate User Experience
You can modify the user experience of the update event by using the onUpdateReady()
function in the JavaScript application code. These modifications include managing the UI that
is shown to the user, text strings, look and feel, position of alert, and so on. You can also add
behaviors such as storing a timestamp of the last time the end user was prompted for an update,
then waiting for some fixed period of time, such as a week, before again prompting the user to
update.
Kapsel Development
19
Kapsel Development
Note: Ensure that any code written for the onUpdateReady() function that defers, or
otherwise overrides, default update life cycle includes an appropriate recovery method, and
does not permanently turn off updates.
Example of Overriding Default Update Behavior
You can assign a custom function to the onUpdateReady() event to override default
update behavior and force an update that does not ask the user to confirm it. It can either go
immediately, or the Administrator can set a date by which it goes.
To do this, add a custom function to onUpdateReady(), for example:
sap.AppUpdate.onupdateready = myCustomAppUpdateFunction
Then, in that custom function, control the update process in whatever way you want. For
example, to automatically load the update without first prompting the user for permission, you
can add something similar to this:
function myCustomAppUpdateFunction = {
// No notification just reload
console.log("Applying application update");
sap.AppUpdate.reloadApp();
}
To use your own custom prompt to warn the user that the app is ready to update, you can do
something similar to this:
function myCustomAppUpdateFunction = (e){
console.log("Confirming application update");
navigator.notification.confirm('Do you want to install the latest
application update?', doAppUpdateContinue, 'Please confirm', 'Yes,
No');
}
function doAppUpdateContinue(buttonNum){
if (buttonNum==1) {
console.log("Applying application update");
sap.AppUpdate.reloadApp();
}
};
20
Kapsel Development
The initial copy from the application bundle functions normally, until the time that
AppUpdate downloads the first revision from the server.
In other words, since the server's auto incremented Revision value starts at 1, a
hybridapprevision value of 0, or an empty value in the config.xml tells the AppUpdate
plugin that it is working with the application bundle copy.
2. If the hybridapprevision on the device (either set in config.xml, or managed by
AppUpdate plugin) is greater than 0, and there is a newer revision on the server, then the
AppUpdate plugin downloads only changed, new, or deleted resourcesa delta
update. The delta calculations are executed by SAP Mobile Platform Server before a
request from the AppUpdate plugin, and are maintained for updating from any available
historical revision on the server to the current revision.
This table shows an example of the update behavior. A valid update path is any distance to
the right on the matrix.
Device hybridapprevi-
Server Revision
sion
0
1
3
4
5
1.2.3/1
1.2.3/2
1.2. 1.3.0/4
3/3
1.5.1/5
2.0.0/6
Full
Full
Full
Full
Full
Full
Delta
Delta
Delta
Delta
Delta
Delta
Delta
Delta
Delta
Delta
Delta
Delta
Delta
Delta
Delta
Best Practices
For most smaller Web applications, you should simply omit the hybridapprevision
parameter from the config.xml. This ensures that the revision numbering on-device
and on the server is correctly aligned. The only full download occurs upon the Kapsel
application bundle's installation and initializationall subsequent downloads will be
deltas.
For large Web applications (tens of MBs or greater), setting the hybridapprevision
parameter in the config.xml can greatly reduce the download volume. You should
ensure that the value on-device matches the correct value for the server. Since the values on
the server are auto incremented, it may be advisable when setting this parameter to
complete the upload on the server before packaging and distributing the Kapsel
application bundle. This ensures that the correct value is used.
Kapsel Development
21
Kapsel Development
Task
Note: The AppUpdate plugin has dependencies on the Logon plugin, as well as some Cordova
plugins. These are automatically added to your project when you add the AppUpdate plugin.
1. Add the AppUpdate plugin by entering the following at the command prompt, or terminal:
On Windows:
cordova -d plugin add <SDK_HOME>\MobileSDK3\KapselSDK
\plugins\appupdate
On Mac:
cordova -d plugin add ~<SDK_HOME>/MobileSDK3/KapselSDK/
plugins/appupdate
Note: The path you enter to the Kapsel plugin must be the absolute path (not relative
path).
2. (Optional) To see a list of installed plugins in your Cordova project, open a command
prompt or terminal window, navigate to your Cordova project folder, and enter:
cordova plugins
The Cordova command line interface returns a JSON array showing installed plugins, for
example:
[ 'org.apache.cordova.core.camera',
'org.apache.cordova.core.device-motion',
'org.apache.cordova.core.file' ]
In this example, the Cordova project has the Cordova core Camera, Accelerator (devicemotion), and File plugins installed.
3. Modify the files in the www folder for the project as necessary, then copy them to the
platform directories by running:
cordova -d prepare android
cordova -d prepare ios
4. Use the Android IDE or Xcode to deploy and run the project.
22
Kapsel Development
Note: If you are using an iOS device, remember to add the "clienthubEntitlements" to the
Keychain Groups in the Entitlement section in Xcode.
Kapsel AppUpdate API Reference
The Kapsel AppUpdate API Reference provides usage information for AppUpdate API
classes and methods, as well as provides sample source code.
AppUpdate namespace
Used to provide server-based updates to the application content.
The AppUpdate plugin updates the contents of the www folder of deployed Kapsel
applications. After an application successfully does a logon to an SAP Mobile Platform 3
server, the AppUpdate plugin is able to download an available update. See Uploading Hybrid
Apps in user documentation for information on how to upload an update to SAP Mobile
Platform 3 server.
After an update is completely downloaded, the application user is prompted to install the
update and restart the application. They can decline if they wish.
To add the AppUpdate plugin to your project, use the following command:
cordova plugin add <path to directory containing Kapsel plugins>\appupdate
To remove the AppUpdate plugin from your project, use the following command:
Kapsel Development
23
Kapsel Development
This means that the local assets in your www folder are uploaded to the server and the server is
reporting revision 1 for them. This allows the application to receive a delta update when
revision 2 is available instead of a full update.
Caveats
It is important to test that your update has valid HTML, Javascript, and CSS. Otherwise, the
update could prevent the application from functioning correctly, and may no longer be
updateable. You can test the updated application in a separate simulator or additional test
device. You can also validate your Javascript with tools like JSLint, or JSHint. You can
validate CSS with CSS Lint.
Methods
24
Name
Description
reloadApp() on page 26
Kapsel Development
reset() on page 27
update() on page 27
Events
Name
Description
checking on page 27
downloading on page 28
error on page 28
noupdate on page 29
updateready on page 29
Source
Type
Description
eventname
string
function
Kapsel Development
25
Kapsel Development
Example
sap.AppUpdate.addEventListener('checking', function(e) {
console.log("Checking for update");
});
Source
Source
Type
Description
eventname
string
function
Example
// Adding the listener
var listener = function(e) {
console.log("Checking for update");
});
sap.AppUpdate.addEventListener('checking', listener);
// Removing the listener
sap.AppUpdate.removeEventListener('checking', listener);
26
Kapsel Development
Source
Source
Source
Type
Default
Description
type
string
undefined
Kapsel Development
27
Kapsel Development
Type
object
Example
sap.AppUpdate.addEventListener('checking', function(e) {
console.log("Checking for update");
});
Source
Type
Default
Description
type
string
undefined
Type
object
Example
sap.AppUpdate.addEventListener('downloading', function(e) {
console.log("Downloading update");
});
Source
28
Name
Type
Default
Description
type
string
undefined
Kapsel Development
statusCode
int
undefined
statusMessage
string
undefined
Type
object
Example
sap.AppUpdate.addEventListener('error', function(e) {
console.log("Error downloading update. statusCode: " +
e.statusCode + " statusMessage: " + e.statusMessage);
});
Source
Type
Default
Description
type
string
undefined
Type
object
Example
sap.AppUpdate.addEventListener('noupdate', function(e) {
console.log("No update");
});
Source
Kapsel Development
29
Kapsel Development
Properties
Name
Type
Default
Description
type
string
undefined
revision
int
undefined
Type
object
Example
// This will listen for updateready event.
// Note: Use sap.AppUpdate.onupdateready if you want to override the
default handler.
sap.AppUpdate.addEventListener('updateready', function(e) {
console.log("Update ready");
});
// Override default handler so that we automatically load the update
// without first prompting the user for permission,
sap.AppUpdate.onupdateready = function(e) {
// No notification just reload
console.log("Apply application update...");
sap.AppUpdate.reloadApp();
};
// Override default handler with custom prompt to warn the user that
the
// application is ready to update.
sap.AppUpdate.onupdateready = function() {
console.log("Confirming application update");
navigator.notification.confirm('Update Available',
function(buttonIndex) {
if (buttonIndex === 2) {
console.log("Applying application update");
sap.AppUpdate.reloadApp();
}
},
"Update", ["Later", "Relaunch Now"]);
};
Source
30
Kapsel Development
appupdate.js
1
// ${project.version}
channel = require('cordova/channel'),
4
is done
5
promptActive = false, // Flag to prevent prompt from
displaying more than once
6
ready
7
8
9
10
var channels = {
11
'checking': channel.create('checking'),
12
'noupdate': channel.create('noupdate'),
13
'downloading': channel.create('downloading'),
14
'error': channel.create('error'),
15
'updateready': channel.create('updateready')
16
};
17
18
// Holds the dom 0 handlers that are registered for the
channels
19
20
21
22
23
if (event.type) {
24
if (event.type in channels) {
25
channels[event.type].fire(event);
26
27
28
}
};
Kapsel Development
31
Kapsel Development
29
30
31
32
/**
33
* Used to provide server-based updates to the application
content.
34
* <br/><br/>
35
* The AppUpdate plugin updates the contents of the www folder
of deployed Kapsel
36
* applications. After an application successfully does a
logon to an SAP Mobile Platform 3
37
* server, the AppUpdate plugin is able to download an
available update. See Uploading Hybrid Apps in user documentation
38
* for information on how to upload an update to SAP Mobile
Platform 3 server.
39
* <br/><br/>
40
* After an update is completely downloaded, the application
user is
41
* prompted to install the update and restart the
application. They can decline
42
* if they wish.
43
* <br/><br/>
44
* Once an update is installed, the application's revision
number is updated.
45
* <br/><br/>
46
47
48
* <a href="http://cordova.apache.org/docs/en/edge/
guide_cli_index.md.html#The%20Command-line%20Interface">Cordova
CLI</a>.<br/>
49
* <br/>
50
* To add the AppUpdate plugin to your project, use the
following command:<br/>
51
* cordova plugin add <path to directory containing Kapsel
plugins>\appupdate<br/>
52
32
* <br/>
Kapsel Development
53
* To remove the AppUpdate plugin from your project, use the
following command:<br/>
54
55
* <br/><br/>
56
57
58
* This is an optional preference that tells the AppUpdate
plugin if the local
59
* assets are uploaded to the server, and at what number.
this preference is
60
If
61
* In your config.xml file you can add the following
preference:<br/>
62
63
<br/>
64
<br/>
65
* This means that the local assets in your www folder are
uploaded to the server
66
* and the server is reporting revision 1 for them.
allows the application
This
67
* to receive a delta update when revision 2 is available,
instead of a full update.
68
* <br/><br/>
69
70
* <b>Caveats</b><br/>
71
* It is important to test that your update has valid HTML,
JavaScript, and CSS.
72
* Otherwise, the update could prevent the application from
functioning correctly,
73
* and may no longer be updateable.
application in a
74
* separate simulator or other test device.
validate your
75
* JavaScript with tools like <a href="http://
www.jslint.com">JSLint</a>, or
76
* <a href="http://www.jshint.com">JSHint</a>.
Kapsel Development
33
Kapsel Development
77
* You can validate CSS with <a href="http://csslint.net">CSS
Lint</a>.
78
* <br/><br/>
79
80
* @namespace
81
* @alias AppUpdate
82
* @memberof sap
83
*/
84
module.exports = {
85
/**
86
* Force an update check. By default, updates occur
automatically during logon and resume.
87
88
* @example
89
* sap.AppUpdate.update();
90
*/
91
update: function () {
92
93
if (logonFired) {
94
sap.Logon.unlock(function (connectionInfo) {
95
96
connectionInfo.applicationId =
sap.Logon.applicationId;
97
98
exec(_eventHandler, null, 'AppUpdate',
'update', [connectionInfo]);
99
});
100
101
}
},
102
103
34
/**
104
resources.
105
* @example
Kapsel Development
106
* sap.AppUpdate.reloadApp();
107
*/
108
reloadApp: function () {
109
110
111
112
/**
113
* Removes all local updates and loads the original Web
assets bundled with the app. Call this after deleteRegistration.
114
* Reset calls error callback if it is called during the
update process.
115
* @example
116
* sap.Logon.core.deleteRegistration(function() {
117
118
* }, function() {});
119
*/
120
121
'reset', []);
122
sap.AppUpdate.reset();
},
123
124
/**
125
* Add a listener for an AppUpdate event.
available event names.
126
event.
127
fired.
128
* @example
129
{
* sap.AppUpdate.addEventListener('checking', function(e)
130
131
* });
132
*/
133
Kapsel Development
35
Kapsel Development
134
if (eventname in channels) {
135
channels[eventname].subscribe(f);
136
137
}
},
138
139
/**
140
* Removes a listener for an AppUpdate event.
for available event names.
See events
141
event.
142
143
* @example
144
145
146
147
* });
148
listener);
* sap.AppUpdate.addEventListener('checking',
149
150
151
listener);
* sap.AppUpdate.removeEventListener('checking',
152
*/
153
154
if (eventname in channels) {
155
channels[eventname].unsubscribe(f);
156
157
}
}
158
159
36
/**
160
update.
161
162
* @event sap.AppUpdate#checking
Kapsel Development
163
* @type {object}
164
* @property {string} type - The name of the event.
is 'checking.'
165
166
{
Value
* @example
* sap.AppUpdate.addEventListener('checking', function(e)
167
168
* });
169
*/
170
171
/**
172
* Event fired when AppUpdate finds no available updates
on the server.
173
174
* @event sap.AppUpdate#noupdate
175
* @type {object}
176
* @property {string} type - The name of the event.
is 'noupdate.'
177
178
{
Value
* @example
* sap.AppUpdate.addEventListener('noupdate', function(e)
179
180
* });
181
*/
console.log("No update");
182
183
/**
184
* Event fired when AppUpdate has found an update and is
starting the download.
185
186
* @event sap.AppUpdate#downloading
187
* @type {object}
188
* @property {string} type - The name of the event.
is 'downloading.'
189
Kapsel Development
Value
* @example
37
Kapsel Development
190
* sap.AppUpdate.addEventListener('downloading',
function(e) {
191
192
* });
193
*/
console.log("Downloading update");
194
195
/**
196
* Event fired when AppUpdate encounters an error while
checking for an update or while downloading an update.
197
this event.
198
199
* @event sap.AppUpdate#error
200
* @type {object}
201
* @property {string} type - The name of the event.
is 'error.'
Value
202
203
message.
204
* @example
205
{
* sap.AppUpdate.addEventListener('error', function(e)
206
*
console.log("Error downloading update. statusCode:
" + e.statusCode + " statusMessage: " + e.statusMessage);
207
* });
208
*/
209
210
/**
211
* Event fired when AppUpdate has a newly downloaded
update available.
212
* A default handler is already added to
sap.AppUpdate.onupdateready that will ask the user to reload the
app.
213
* When using this event, you should call
sap.AppUpdate.reloadApp() to apply the downloaded update.
38
214
215
* @event sap.AppUpdate#updateready
Kapsel Development
216
* @type {object}
217
* @property {string} type - The name of the event.
is 'updateready.'
Value
218
downloaded.
219
* @example
220
221
222
* // Note: Use sap.AppUpdate.onupdateready if you want to
override the default handler.
223
* sap.AppUpdate.addEventListener('updateready',
function(e) {
224
225
* });
226
console.log("Update ready");
227
* // Override the default handler so that the update is
automatically loaded,
228
229
* sap.AppUpdate.onupdateready = function(e) {
230
231
232
sap.AppUpdate.reloadApp();
233
* };
234
235
* // Override the default handler with a custom prompt to
notify the user that the
236
237
* sap.AppUpdate.onupdateready = function() {
238
239
Available',
navigator.notification.confirm('Update
240
241
242
");
function(buttonIndex) {
if (buttonIndex === 2) {
Kapsel Development
39
Kapsel Development
243
244
245
},
246
247
* };
248
*/
249
sap.AppUpdate.reloadApp();
}
};
250
251
252
253
function defineSetGet(eventType) {
254
function () {
module.exports.__defineGetter__("on" + eventType,
255
return domZeroHandlers[eventType];
256
});
257
258
module.exports.__defineSetter__("on" + eventType,
function (val) {
259
260
if (domZeroHandlers[eventType]) {
261
module.exports.removeEventListener(eventType,
domZeroHandlers[eventType]);
262
263
264
265
if (val) {
266
domZeroHandlers[eventType] = val;
267
module.exports.addEventListener(eventType,
domZeroHandlers[eventType]);
268
269
270
});
}
271
272
40
defineSetGet(type);
Kapsel Development
273
274
275
276
module.exports.onupdateready = function () {
277
if (!promptActive) {
278
promptActive = true;
279
280
281
promptActive = false;
282
if (buttonIndex === 2) {
283
284
{
sap.Logon.unlock(function (connectionInfo)
285
call
286
sap.Logon.applicationId;
connectionInfo.applicationId =
287
288
module.exports.reloadApp();
289
});
290
291
}
}
292
293
if (!bundle) {
294
295
var i18n =
require('com.sap.mp.cordova.plugins.i18n.i18n');
296
bundle = i18n.load({
297
path: "plugins/
com.sap.mp.cordova.plugins.appupdate/www"
298
299
});
}
300
301
Kapsel Development
window.navigator.notification.confirm(
41
Kapsel Development
302
bundle.get("update_available"),
303
onConfirm,
304
bundle.get("update"), [bundle.get("later"),
bundle.get("relaunch_now")]);
305
306
}
}
307
308
309
{
document.addEventListener("onSapLogonSuccess", function ()
310
logonFired = true;
311
module.exports.update();
312
}, false);
313
314
document.addEventListener("onSapResumeSuccess",
module.exports.update, false);
42
Kapsel Development
Task
1. Add the plugin, by entering, at the command prompt:
On Windows:
cordova -d plugin add <SDK_HOME>\MobileSDK3\KapselSDK
\plugins\logon
On Mac:
cordova -d plugin add ~<SDK_HOME>/MobileSDK3/KapselSDK/
plugins/logon
Note: The path you enter to the Kapsel plugin must be the absolute path (not relative
path).
2. (Optional) To see a list of installed plugins in your Cordova project, open a command
prompt or terminal window, navigate to your Cordova project folder, and enter:
cordova plugins
The Cordova command line interface returns a JSON array showing installed plugins, for
example:
[ 'com.sap.mp.cordova.plugins.corelibs',
'com.sap.mp.cordova.plugins.logon',
'org.apache.cordova.console',
'org.apache.cordova.device',
'org.apache.cordova.device-orientation',
'org.apache.cordova.dialogs',
'org.apache.cordova.inappbrowser' ]
In this case, some core Cordova plugins were added, including corelibs, console, device,
device-orientation, dialogs, and inAppBrowser. CoreLibs is a utility plugin that is
automatically added to every Kapsel project by the command line interface, so you need
never add the CoreLibs plugin to a project manually.
3. Configure the application in Management Cockpit.
4. Define a variable in the JavaScript code (typically, this is done in the index.html file of
your Cordova application) to describe the app ID, for example:
var appId = "com.sap.kapsel.mykapselapp";
Kapsel Development
43
Kapsel Development
Kapsel uses an app ID to tell the server which application definition on the server to use for
this application. The app ID that is defined on the server must match what is entered here.
5. Define the connection to the server, for example:
var defaultContext = {
"serverHost" : "192.168.254.159",
"https" : "false",
"serverPort" : "8080",
};
This prepopulates the fields in the registration dialog that is shown to users during the
initialization process.
6. Make a call to the Logon plugins init method as shown:
//Make call to Logon's Init method to get things registered and
all set up
sap.Logon.init(logonSuccess, logonError, appId, defaultContext);
The init method gathers information about the environments security configuration by
asking the Afaria client and Client Hub application, if available, sets up and configures the
DataVault, connects to the server to register the application connection and authenticate
the user. As part of this process, the appropriate screens are shown to gather user input and
manage the entire process.
7. Verify the registration in Management Cockpit.
a) Log in to Management Cockpit.
b) Click Applications.
c) Click Registrations.
You can see the registration ID following a successful registration.
8. Use the Android IDE or Xcode to deploy and run the project.
44
Kapsel Development
Note: If you are deploying to an iOS device, in Xcode, you must add the
clienthubEntitlements and $(CFBundleIdentifier) to the keychain group in the
Entitlements section as well as the bundle identifier.
document.addEventListener("deviceready", deviceReady,
This example shows the call to the sap.Logon.init function, as well as the success
and error callbacks that are passed to the sap.Logon.init function. It also shows how
you can make sure the registration process is started as soon as possible by attaching a
Kapsel Development
45
Kapsel Development
listener to the deviceready event. Inside the deviceReady function, the app ID and
the context are defined.
3. Save the file.
Running the Logon Application on iOS
Deploy and run the Logon project on iOS.
1. In a terminal window, make sure you are in the project folder and execute the
command:
cordova prepare ios
2. Open Xcode.
3. In a Finder window, browse to your Cordova project folder, <Project Name>/
platforms/ios.
4. Add the clienthubEntitlements keychain group to the Entitlements section of the
project.
This shows an example:
46
Kapsel Development
1. Open the StaticScreens.js file, which is located in SDK_HOME
\MobileSDK3\KapselSDK\plugins\logon\www\common\modules.
2. Find the SCR_REGISTRATION screen and reorder, or hide and show fields using the
visible:false options. You can also delete unneeded entries.
For example:
SCR_REGISTRATION': {
id: 'SCR_REGISTRATION',
fields: {
user : {
uiKey:'FLD_USER'
},
password : {
uiKey:'FLD_PASS',
type: 'password'
},
serverHost : {
uiKey:'FLD_HOST',
editable:true
},
serverPort : {
uiKey:'FLD_PORT',
type: 'number',
editable:true,
visible:true
},
communicatorId : {
uiKey: 'FLD_COMMUNICATORID',
'default':'REST',
visible:false
},
https: {
uiKey:'FLD_IS_HTTPS',
type: 'switch',
'default':false,
visible:false
},
},
Kapsel Development
47
Kapsel Development
The logon plugin is a component of the SAP Mobile Application Framework (MAF), exposed
as a Cordova plugin. The basic idea is that it provides screen flows where the user can enter the
values needed to connect to an SAP Mobile Platform 3.0 server and stores those values in its
own secure data vault. This data vault is separate from the one provided with the encrypted
storage plugin. In an OData based SAP Mobile Platform 3.0 application, a client must onboard
or register with the SAP Mobile Platform 3.0 server to receive an application connection ID
for a particular app. The application connection ID must be sent along with each request that is
proxied through the SAP Mobile Platform 3.0 server to the OData producer.
To add the Logon plugin to your project, use the following command:
cordova plugin add <full path to directory containing Kapsel plugins>\logon
To remove the Logon plugin from your project, use the following command:
cordova plugin rm com.sap.mp.cordova.plugins.logon
Methods
48
Name
Description
This method will launch the UI screen for application users to manage and update the back end
passcode that Logon stores in the data vault that is
used to authenticate the client to the server.
Get an (JSON serializable) object from the DataVault for a given key.
Kapsel Development
managePasscode( onsuccess, onerror ) on page
56
This method will launch the UI screen for application users to manage and update the data vault
passcode or, if the SMP server's Client Passcode
Policy allows it, enable or disable the passcode to
the data vault.
Type Definitions
Name
Description
Callback function that is invoked upon successfully retrieving an object from the DataVault.
Callback function that is invoked upon successfully registering or unlocking or retrieving the
context.
successCallbackNoParameters on page 65
Source
Kapsel Development
49
Kapsel Development
Source
Syntax
<static> core
Example
var successCallback = function(result){
alert("Result: " + JSON.stringify(result));
}
var errorCallback = function(errorInfo){
alert("Error: " + JSON.stringify(errorInfo));
}
sap.Logon.core.getState(successCallback,errorCallback);
sap.Logon.core.getContext(successCallback,errorCallback);
sap.Logon.core.deleteRegistration(successCallback,errorCallback);
Source
50
Kapsel Development
Type
Description
onsuccess
sap.Logon~successCallbackNoParameters on page 65
onerror
sap.Logon~errorCallback on
page 59
Example
var errorCallback = function(errorInfo){
alert("Error: " + JSON.stringify(errorInfo));
}
var successCallback = function(context){
alert("Password successfully changed.");
}
sap.Logon.changePassword(successCallback,errorCallback);
Source
Kapsel Development
Type
Description
51
Kapsel Development
onsuccess
sap.Logon~getSuccessCallback on page 60
onerror
sap.Logon~errorCallback on
page 59
key
string
Example
var errorCallback = function(errorInfo){
alert("Error: " + JSON.stringify(errorInfo));
}
var getSuccess = function(value){
alert("value retrieved from the store: " +
JSON.stringify(value));
}
var setSuccess = function(){
sap.Logon.get(getSuccess,errorCallback,'someKey');
}
sap.Logon.set(setSuccess,errorCallback,'someKey', 'some string
(could also be an object).');
Source
52
Type
Argument
Default
Description
Kapsel Development
successCallback
sap.Logon~successCallback on
page 60
errorCallback
sap.Logon~errorCallback on page
59
applicationId
string
The unique ID of
the application.Must match
the application ID
on the SAP Mobile Platform
server.
Kapsel Development
53
Kapsel Development
context
object
(optional)
sap.Logon~successCallback on
page 60 for the
structure of the
context object.
Note that all properties of the context object are optional, and you
only need to specify the properties
for which you
want to provide
default values for.
The values will be
presented to the
application users
during the registration process
and given them a
chance to override
these values during runtime.
54
Kapsel Development
logonView
string
(optional)
"com/sap/mp/
logon/iabui"
Example
// a custom UI can be loaded here
var logonView = sap.logon.IabUi;
// The app ID
var applicationId = "someAppID";
// You only need to specify the fields for which you want to set the
default.
These values are optional because they will be
// used to prefill the fields on Logon's UI screen.
var defaultContext = {
"serverHost" : "defaultServerHost.com"
\t"https" : false,
\t"serverPort" : "8080",
\t"user" : "user1",
\t"password" : "Zzzzzz123",
\t"communicatorId" : "REST",
\t"securityConfig" : "sec1",
\t"passcode" : "Aaaaaa123",
\t"unlockPasscode" : "Aaaaaa123"
};
var app_context;
var successCallback = function(context){
Kapsel Development
55
Kapsel Development
app_context = context;
Source
Type
Description
onsuccess
sap.Logon~successCallbackNoParameters on page 65
onerror
sap.Logon~errorCallback on
page 59
Example
var errorCallback = function(errorInfo){
alert("Error: " + JSON.stringify(errorInfo));
}
var successCallback = function(){
alert("Locked!");
}
sap.Logon.lock(successCallback,errorCallback);
Source
56
Kapsel Development
Parameters
Name
Type
Description
onsuccess
sap.Logon~successCallbackNoParameters on page 65
onerror
sap.Logon~errorCallback on
page 59
Example
var errorCallback = function(errorInfo){
alert("Error: " + JSON.stringify(errorInfo));
}
var successCallback = function(context){
alert("Passcode successfully managed.");
}
sap.Logon.managePasscode(successCallback,errorCallback);
Source
Type
Description
onsuccess
sap.Logon~successCallbackNoParameters on page 65
onerror
sap.Logon~errorCallback on
page 59
key
string
value
object
Kapsel Development
57
Kapsel Development
Example
var errorCallback = function(errorInfo){
alert("Error: " + JSON.stringify(errorInfo));
}
var getSuccess = function(value){
alert("value retrieved from the store: " +
JSON.stringify(value));
}
var setSuccess = function(){
sap.Logon.get(getSuccess,errorCallback,'someKey');
}
sap.Logon.set(setSuccess,errorCallback,'someKey', 'some string
(could also be an object).');
Source
Type
Description
onsuccess
sap.Logon~successCallbackNoParameters on page 65
onerror
sap.Logon~errorCallback on
page 59
Example
var errorCallback = function(errorInfo){
alert("Error: " + JSON.stringify(errorInfo));
}
var successCallback = function(context){
alert("The showRegistrationData screenflow was successful.");
}
sap.Logon.showRegistrationData(successCallback,errorCallback);
Source
58
Kapsel Development
Type
Description
onsuccess
onerror
sap.Logon~errorCallback on
page 59
Example
var errorCallback = function(errorInfo){
alert("Error: " + JSON.stringify(errorInfo));
}
var successCallback = function(context){
alert("Registered and unlocked. Context: " +
JSON.stringify(context));
}
sap.Logon.unlock(successCallback,errorCallback);
Source
Kapsel Development
59
Kapsel Development
Syntax
errorCallback( errorObject )
Parameters
Name
Type
Description
errorObject
Object
Source
Type
Description
value
Object
Source
60
Kapsel Development
Syntax
successCallback( context )
Parameters
Name
Kapsel Development
Type
Description
61
Kapsel Development
context
Object
"registrationContext": {
"resourcePath": Resource
path on the server. The path is
used mainly for path based reverse proxy but can contain a
custom relay server path as well.
62
Kapsel Development
"farmId": FarmId of the
server. Can be nil. Used in case
of Relay server or SiteMinder.
"communicatorId": Id of the
communicator manager that
will be used for performing the
logon. Possible values: IMO /
GATEWAY / REST
"securityConfig": Security
configuration. If nil, the default
configuration is used.
"gatewayPingPath": The
custom path of the ping URL on
the gateway. Used in case of
Gateway only registration
mode.
Kapsel Development
63
Kapsel Development
"applicationEndpointURL":
Contains the application endpoint URL after a successful
registration.
"applicationConnectionId": ID
to get after a successful SUP
REST registration. Needs to be
set in the download request
header with key X-SUP-APPCID
"afariaRegistration": manual /
automatic / certificate
"alwaysOn":
"alwaysOff":
"defaultOn":
"hasDigits":
"hasLowerCaseLetters":
"hasSpecialLetters":
"hasUpperCaseLetters":
"defaultAllowed":
64
Kapsel Development
"expirationDays":
"lockTimeout":
"minLength":
"minUniqueChars":
"retryLimit":
"policyReadOnly": specifies
whether passcode policy is
coming from afaria
"credentialsByClientHub":
specifies whether credentials
are coming from clientHub
Source
Kapsel Development
65
Kapsel Development
LogonController.js
1
2
3
5
6
var _oLogonCore;
var _oLogonView;
9
10
var _providedContext;
11
12
var init = function (successCallback, errorCallback,
applicationId, context, customView) {
13
14
15
document.addEventListener("resume",
16
function(){
17
resume(
18
arguments);},
function() { fireEvent('onSapResumeSuccess',
19
arguments);}
function() { fireEvent('onSapResumeError',
20
);
21
},
22
false);
23
24
// The success callback used for the call to
_oLogonCore.initLogon(...)
25
26
initialized.');
27
66
Kapsel Development
28
// Now that Logon is initialized, registerOrUnlock
is automatically called.
29
errorCallback );
30
registerOrUnlock( successCallback,
}
31
32
33
pass it along.
34
callback with.
35
if( error ) {
36
errorCallback( error );
37
} else {
38
errorCallback( utils.Error('ERR_INIT_FAILED') );
39
40
}
}
41
42
utils.log('LogonController.init enter');
43
utils.log(applicationId);
44
module.exports.applicationId = applicationId;
45
46
47
if( context ){
48
49
_providedContext = context;
}
50
51
_oLogonView = customView;
52
if (!_oLogonView) {
53
54
_oLogonView = sap.logon.IabUi;
}
55
Kapsel Development
67
Kapsel Development
56
//
coLogonCore.cordova.require("com.sap.mp.cordova.plugins.logon.Logon
Core");
57
_oLogonCore = sap.logon.Core;
58
_oLogonCore.initLogon(initSuccess, initError,
applicationId);
59
60
61
module.exports.core = _oLogonCore;
62
63
64
65
66
67
68
69
if (!window.CustomEvent) {
70
eventInitDict) {
window.CustomEvent = function(type,
71
var newEvent =
document.createEvent('CustomEvent');
72
newEvent.initCustomEvent(
73
type,
74
eventInitDict.bubbles),
!!(eventInitDict &&
75
eventInitDict.cancelable),
!!(eventInitDict &&
76
null));
(eventInitDict ? eventInitDict.detail :
77
return newEvent;
78
79
};
}
80
81
var event = new CustomEvent(eventId, { 'detail':
{ 'id': eventId, 'args': args }});
82
68
Kapsel Development
83
setTimeout(function() {
84
document.dispatchEvent(event);
85
}, 0);
86
} else {
87
throw 'Invalid eventId: ' +
JSON.stringify(event);
88
89
}
}
90
91
var FlowRunner = function(onsuccess, onerror, pLogonView,
pLogonCore) {
92
93
var onFlowSuccess;
94
var onFlowError;
95
var onFlowCancel;
96
97
var logonView;
98
var logonCore;
99
var flow;
100
101
102
103
logonView = pLogonView;
104
logonCore = pLogonCore;
105
106
107
utils.logJSON('onFlowSuccess');
108
logonView.close();
109
onsuccess.apply(this, arguments);
110
111
112
113
Kapsel Development
69
Kapsel Development
114
logonView.close();
115
onerror.apply(this, arguments);
116
117
118
119
utils.logJSON('onFlowCancel');
120
//logonView.close();
121
onFlowError(new
utils.Error('ERR_USER_CANCELLED'));
122
123
124
125
126
handleCoreResult(null, currentState);
}
127
128
var handleCoreResult = function (currentContext,
currentState) {
129
if (typeof currentContext === undefined)
currentContext = null;
130
131
132
if (currentState) {
133
if (currentContext &&
currentContext.policyContext &&
currentContext.policyContext.defaultAllowed){
134
true;
currentState.defaultPasscodeAllowed =
135
136
else {
137
false;
currentState.defaultPasscodeAllowed =
138
139
}
}
140
70
Kapsel Development
141
currentContext');
utils.logJSON(currentContext, 'handleCoreResult
142
currentState');
utils.logJSON(currentState, 'handleCoreResult
143
144
145
utils.logJSON(flow.name);
146
147
148
149
150
ruleMatching:
151
152
153
154
//utils.logJSON(rule, 'rule');
155
156
'rule.condition');
//utils.logJSON(rule.condition,
157
{
158
transition rule';
159
160
161
162
163
if (currentState)
164
165
(and rule) mismatch
166
167
//else {
168
//
Kapsel Development
// match:
71
Kapsel Development
169
&&
//
170
//
// (typeof currentState ===
'undefined') // null or undefined
171
172
173
currentState){
//}
}
else if (rule.condition.state !== 'undefined' &&
174
utils.log('stateMatching');
175
176
stateMatching:
177
178
utils.log(field);
179
currentState[field])
if (rule.condition.state[field] ===
180
181
field);
182
field match
183
184
else {
185
field);
186
(and rule) mismatch
187
};
188
189
}
}
190
191
192
if (currentContext)
193
194
context (and rule) mismatch
195
72
Kapsel Development
196
//else {
197
//
198
&&
// match:
//
199
//
// (typeof currentContext ===
'undefined') // null or undefined
200
201
//}
}
202
else if (rule.condition.context !== 'undefined'
&& currentContext){
203
204
utils.log('contextMatching');
205
contextMatching:
206
207
utils.log(field);
208
currentContext[field])
if (rule.condition.context[field] ===
209
210
field);
211
field match
continue contextMatching;
212
213
else {
214
field);
// context
215
field (and rule) mismatch
continue ruleMatching;
216
// context
};
217
218
219
utils.log('match found');
220
utils.logJSON(rule, 'rule');
221
222
Kapsel Development
73
Kapsel Development
223
rule.action(currentContext);
224
225
226
227
228
screenId);
utils.log('handleCoreResult: ' +
229
utils.logKeys(flow.screenEvents[screenId]);
230
if(!currentContext){
231
currentContext = {};
232
233
234
_providedContext ){
235
// The current registrationContext is
null, and we have been given a context when initialized,
236
237
_providedContext;
currentContext.registrationContext =
238
} else if
(currentContext.registrationContext && _providedContext && !
currentContext.registrationReadOnly){
239
240
//if (!
currentContext.registrationContext[key]){
241
currentContext.registrationContext[key] = _providedContext[key];
242
//}
243
244
245
246
logonView.showScreen(screenId,
flow.screenEvents[screenId], currentContext);
247
248
74
Kapsel Development
249
else {
250
onFlowError(new
utils.Error('ERR_INVALID_ACTION'));
251
252
253
matchFound = true;
254
break ruleMatching;
255
256
257
if (!matchFound) {
258
onFlowError(new
utils.Error('ERR_INVALID_STATE'));
259
260
261
262
263
this.run = function(FlowClass) {
264
utils.log('FlowRunner.run '
+ FlowClass.name);
265
flow = new FlowClass(logonCore, logonView,
handleCoreResult, onFlowSuccess, onFlowError, onFlowCancel);
266
267
onFlowError);
logonCore.getState(handleCoreStateOnly,
268
269
270
271
272
273
var MockFlow = function MockFlow(logonCore, logonView,
onCoreResult, onFlowSuccess, onFlowError, onFlowCancel) {
274
//wrapped into a function to defer evaluation of the
references to flow callbacks
275
276
Kapsel Development
75
Kapsel Development
277
this.name = 'mockFlowBuilder';
278
279
this.stateTransitions = [
280
281
condition: {
282
state: {
283
secureStoreOpen: false,
284
285
},
286
action: 'SCR_MOCKSCREEN'
287
},
288
289
condition: {
290
state: {
291
secureStoreOpen: true,
292
293
},
294
action: 'SCR_MOCKSCREEN'
295
},
296
297
];
298
299
this.screenEvents = {
300
'SCR_TURN_PASSCODE_ON': {
301
onsubmit: onFlowSuccess,
302
oncancel: onFlowCancel,
303
onerror: onFlowError,
304
305
}
};
306
76
307
308
//return flow;
Kapsel Development
309
310
311
var RegistrationFlow = function
RegistrationFlow(logonCore, logonView, onCoreResult, onFlowSuccess,
onFlowError, onFlowCancel) {
312
//wrapped into a function to defer evaluation of the
references to flow callbacks
313
314
this.name = 'registrationFlowBuilder';
315
316
317
318
319
onFlowError(errorWithDomainCodeDescription("MAFLogon","0","SSO
Passcode set screen was cancelled"));
320
321
322
323
onFlowError(errorWithDomainCodeDescription("MAFLogon","1","Registra
tion screen was cancelled"));
324
325
326
// internal methods
327
328
return function(coreContext) {
329
logonView.showScreen(screenId,
this.screenEvents[screenId], coreContext);
330
331
}.bind(this);
}.bind(this);
332
333
334
utils.logJSON(context,
'logonCore.unlockSecureStore');
Kapsel Development
77
Kapsel Development
335
logonCore.unlockSecureStore(onCoreResult,
onUnlockError, context)
336
337
338
339
utils.logJSON("onUnlockError: " +
JSON.stringify(error));
340
341
codes
342
logonView.showNotification("ERR_UNLOCK_FAILED");
343
344
345
346
347
348
if (registrationInProgress == true) {
349
utils.log('back button pushed, no operation
is required as registration is running');
350
351
else {
352
onCancelRegistration();
353
354
355
356
{
357
default passcode');
utils.log('logonCore.unlockSecureStore -
358
var unlockContext =
{"unlockPasscode":null};
359
logonCore.unlockSecureStore(onCoreResult,
onFlowError, unlockContext)
360
361
78
Kapsel Development
362
363
onCoreResult(context, state);
364
registrationInProgress = false;
365
366
367
368
failed');
utils.logJSON(error, 'registration
369
logonView.showNotification(getRegistrationErrorText(error));
370
371
registrationInProgress = false;
}
372
373
374
utils.logJSON(context,
'logonCore.startRegistration');
375
registrationInProgress = true;
376
onRegError, context)
logonCore.startRegistration(onRegSucceeded,
377
378
379
380
utils.logJSON(context,
'logonCore.persistRegistration');
381
logonCore.persistRegistration(onCoreResult,
onCreatePasscodeError, context);
382
383
384
{
385
utils.logJSON("onCancelRegistrationError: " +
JSON.stringify(error));
386
logonView.showNotification(getRegistrationCancelError(error));
387
388
Kapsel Development
79
Kapsel Development
389
390
utils.logJSON("onCreatePasscodeError: " +
JSON.stringify(error));
391
logonView.showNotification(getSecureStoreErrorText(error));
392
393
394
395
utils.logJSON("onSSOPasscodeSetError: " +
JSON.stringify(error));
396
logonView.showNotification(getSSOPasscodeSetErrorText(error));
397
398
399
400
utils.log('logonCore.getContext');
401
onFlowError);
logonCore.getContext(onCoreResult,
402
403
404
405
406
function(result){
var getContextSuccessCallback =
407
408
if(!_hasLogonSuccessEventFired) {
409
arguments);
fireEvent("onSapLogonSuccess",
410
_hasLogonSuccessEventFired = true;
411
412
413
80
onFlowSuccess(result);
414
415
utils.log('logonCore.getContext');
Kapsel Development
416
logonCore.getContext(getContextSuccessCallback, onFlowError);
417
418
419
420
utils.log('logonCore.deleteRegistration');
421
onFlowError);
logonCore.deleteRegistration(onFlowError,
422
423
424
425
utils.log('forgotSSOPin');
426
logonView.showNotification("ERR_FORGOT_SSO_PIN");
427
428
429
430
utils.logJSON('logonCore.skipClientHub');
431
onFlowError);
logonCore.skipClientHub(onCoreResult,
432
433
434
function(context){
var callPersistWithDefaultPasscode =
435
utils.logJSON(context,
'logonCore.persistRegistration');
436
context.passcode = null;
437
logonCore.persistRegistration(
438
onCoreResult,
439
onFlowError,
440
context)
441
442
443
Kapsel Development
// exported properties
81
Kapsel Development
444
this.stateTransitions = [
445
446
condition: {
447
state: {
448
secureStoreOpen: false,
449
status: 'fullRegistered',
450
defaultPasscodeUsed: true
451
452
},
453
action: onUnlockVaultWithDefaultPasscode
454
},
455
456
457
condition: {
458
state: {
459
secureStoreOpen: false,
460
status: 'fullRegistered'
461
462
},
463
action: 'SCR_UNLOCK'
464
},
465
466
467
468
condition: {
469
state: {
470
clarify
471
status: 'fullRegistered',
472
stateClientHub: 'availableNoSSOPin'
473
474
82
}
},
Kapsel Development
475
action: 'SCR_SSOPIN_SET'
476
},
477
478
condition: {
479
state: {
480
secureStoreOpen: false,
481
status: 'new'
482
},
483
context: null
484
},
485
action: callGetContext
486
},
487
488
489
condition: {
490
state: {
491
secureStoreOpen: false,
492
status: 'new',
493
stateClientHub: 'availableNoSSOPin'
494
495
},
496
action: 'SCR_SSOPIN_SET'
497
},
498
499
500
condition: {
501
state: {
502
secureStoreOpen: false,
503
status: 'new',
504
'availableInvalidSSOPin'
stateClientHub:
505
Kapsel Development
83
Kapsel Development
506
},
507
action: 'SCR_SSOPIN_SET'
508
},
509
510
511
condition: {
512
state: {
513
secureStoreOpen: false,
514
status: 'new',
515
'availableValidSSOPin'
stateClientHub:
516
},
517
context : {
518
credentialsByClientHub : true,
519
registrationReadOnly : true
520
521
},
522
action: function(context){
523
utils.logJSON(context,
'logonCore.startRegistration');
524
logonCore.startRegistration(onCoreResult,
onFlowError, context.registrationContext);
525
526
},
527
528
condition: {
529
state: {
530
secureStoreOpen: false,
531
status: 'new',
532
'availableValidSSOPin',
stateClientHub:
533
'initializationSuccessful'
stateAfaria:
534
84
},
Kapsel Development
535
context : {
536
registrationReadOnly : true,
537
afariaRegistration: 'certificate'
538
539
},
540
action: function(context){
541
utils.logJSON(context,
'logonCore.startRegistration');
542
logonCore.startRegistration(onCoreResult,
onFlowError, context.registrationContext);
543
544
},
545
546
condition: {
547
state: {
548
secureStoreOpen: false,
549
status: 'new',
550
'availableValidSSOPin'
stateClientHub:
551
},
552
context : {
553
registrationReadOnly :true,
554
credentialsByClientHub : false
555
556
},
557
action: 'SCR_ENTER_CREDENTIALS'
558
},
559
560
561
562
condition: {
563
state: {
564
Kapsel Development
secureStoreOpen: false,
85
Kapsel Development
565
status: 'new',
566
//stateClientHub: 'notAvailable' |
'availableValidSSOPin' | 'skipped' | 'error'
567
stateAfaria: 'initializationFailed'
568
569
},
570
action: 'SCR_REGISTRATION'
571
},
572
573
574
condition: {
575
state: {
576
secureStoreOpen: false,
577
status: 'new',
578
//stateClientHub: 'notAvailable' |
'availableValidSSOPin' | 'skipped' | 'error'
579
580
},
581
action: 'SCR_REGISTRATION'
582
},
583
584
585
condition: {
586
state: {
587
secureStoreOpen: false,
588
status: 'new',
589
//stateClientHub: 'notAvailable' |
'availableValidSSOPin' | 'skipped' | 'error'
590
stateAfaria: 'initializationFailed'
591
592
},
593
action: 'SCR_REGISTRATION'
594
86
},
Kapsel Development
595
596
597
condition: {
598
state: {
599
secureStoreOpen: false,
600
status: 'registered',
601
defaultPasscodeUsed: true,
602
//
defaultPasscodeAllowed: true,
603
604
},
605
action: 'SCR_SET_PASSCODE_OPT_OFF'
606
},
607
608
condition: {
609
state: {
610
secureStoreOpen: false,
611
status: 'registered',
612
defaultPasscodeUsed: false,
613
defaultPasscodeAllowed: true,
614
615
},
616
action: 'SCR_SET_PASSCODE_OPT_ON'
617
},
618
619
condition: {
620
state: {
621
secureStoreOpen: false,
622
status: 'registered',
623
//
defaultPasscodeAllowed: false,
624
625
},
626
action: 'SCR_SET_PASSCODE_MANDATORY'
Kapsel Development
87
Kapsel Development
627
},
628
629
630
631
condition: {
632
state: {
633
clarify
634
status: 'fullRegistered',
635
'availableInvalidSSOPin'
stateClientHub:
636
637
},
638
action: 'SCR_SSOPIN_CHANGE'
639
},
640
641
condition: {
642
state: {
643
secureStoreOpen: true,
644
status: 'fullRegistered',
645
stateClientHub: 'notAvailable'
646
647
},
648
action: onFullRegistered
649
},
650
651
condition: {
652
state: {
653
secureStoreOpen: true,
654
status: 'fullRegistered',
655
'availableValidSSOPin'
stateClientHub:
656
88
Kapsel Development
657
},
658
action: onFullRegistered
659
},
660
661
condition: {
662
state: {
663
secureStoreOpen: true,
664
status: 'fullRegistered',
665
stateClientHub: 'skipped'
666
667
},
668
action: onFullRegistered
669
},
670
671
672
673
];
674
675
676
this.screenEvents = {
'SCR_SSOPIN_SET': {
677
onsubmit: function(context){
678
'logonCore.setSSOPasscode');
utils.logJSON(context,
679
logonCore.setSSOPasscode(onCoreResult,
onSSOPasscodeSetError, context);
680
},
681
oncancel: onCancelSSOPin,
682
onerror: onFlowError,
683
onforgot: onForgotSsoPin,
684
onskip: onSkipSsoPin
685
},
686
687
Kapsel Development
'SCR_SSOPIN_CHANGE': {
89
Kapsel Development
688
onsubmit: function(context){
689
'logonCore.setSSOPasscode');
utils.logJSON(context,
690
logonCore.setSSOPasscode(onCoreResult,
onSSOPasscodeSetError, context);
691
},
692
oncancel: onSkipSsoPin,
693
onerror: onFlowError,
694
onforgot: onForgotSsoPin
695
},
696
697
'SCR_UNLOCK': {
698
onsubmit: onUnlockSubmit,
699
oncancel: noOp,
700
onerror: onFlowError,
701
onforgot: onForgotAppPasscode,
702
onerrorack: noOp
703
},
704
705
'SCR_REGISTRATION':
706
onsubmit: onRegSubmit,
707
oncancel: onCancelRegistration,
708
onerror: onFlowError,
709
onbackbutton: onRegistrationBackButton
710
},
711
712
713
onsubmit: onRegSubmit,
714
oncancel: onCancelRegistration,
715
onerror: onFlowError
716
},
717
'SCR_SET_PASSCODE_OPT_ON': {
718
90
'SCR_ENTER_CREDENTIALS' : {
onsubmit: onCreatePasscodeSubmit,
Kapsel Development
719
oncancel: noOp,
720
onerror: onFlowError,
721
ondisable:
showScreen('SCR_SET_PASSCODE_OPT_OFF'),
722
onerrorack: noOp
723
},
724
'SCR_SET_PASSCODE_OPT_OFF': {
725
onsubmit:
callPersistWithDefaultPasscode,
726
oncancel: noOp,
727
onerror: onFlowError,
728
onenable:
showScreen('SCR_SET_PASSCODE_OPT_ON'),
729
onerrorack: noOp
730
},
731
'SCR_SET_PASSCODE_MANDATORY': {
732
onsubmit: onCreatePasscodeSubmit,
733
oncancel: noOp,
734
onerror: onFlowError,
735
onerrorack: noOp
736
},
737
738
739
740
};
741
742
743
744
745
746
747
Kapsel Development
91
Kapsel Development
748
var ChangePasswordFlow = function
ChangePasswordFlow(logonCore, logonView, onCoreResult,
onFlowSuccess, onFlowError, onFlowCancel) {
749
//wrapped into a function to defer evaluation of the
references to flow callbacks
750
751
this.name = 'changePasswordFlowBuilder';
752
753
754
// internal methods
755
756
757
unlock');
758
registerOrUnlock(onCoreResult,onFlowError);
759
760
761
762
utils.logJSON(context,
'logonCore.changePassword');
763
context
764
onFlowError, context);
logonCore.changePassword(onPasswordChanged,
765
766
767
768
769
utils.log('onPasswordChanged');
770
onFlowError);
logonCore.getContext(onFlowSuccess,
771
772
92
773
// exported properties
774
this.stateTransitions = [
Kapsel Development
775
776
condition: {
777
state: {
778
secureStoreOpen: false,
779
780
},
781
action: callUnlockFlow,
782
},
783
784
condition: {
785
state: {
786
secureStoreOpen: true,
787
788
},
789
action: 'SCR_CHANGE_PASSWORD'
790
},
791
792
];
793
794
this.screenEvents = {
795
'SCR_CHANGE_PASSWORD': {
796
onsubmit: onChangePasswordSubmit,
797
oncancel: onFlowCancel,
798
onerror: onFlowError
799
800
};
801
802
803
804
805
Kapsel Development
93
Kapsel Development
806
var ManagePasscodeFlow = function
ManagePasscodeFlow(logonCore, logonView, onCoreResult,
onFlowSuccess, onFlowError, onFlowCancel) {
807
//wrapped into a function to defer evaluation of the
references to flow callbacks
808
809
this.name = 'managePasscodeFlowBuilder';
810
811
// internal methods
812
813
return function(coreContext) {
814
logonView.showScreen(screenId,
this.screenEvents[screenId], coreContext);
815
816
}.bind(this);
}.bind(this);
817
818
819
820
utils.logJSON(context,
'logonCore.changePasscode');
821
logonCore.changePasscode(
822
onCoreResult,
823
onChangePasscodeError,
824
context)
825
826
827
828
utils.logJSON("onChangePasscodeError: " +
JSON.stringify(error));
829
logonView.showNotification(getSecureStoreErrorText(error));
830
831
832
833
94
Kapsel Development
834
835
utils.logJSON(context,
'logonCore.disablePasscode');
836
context.passcode = null;
837
logonCore.changePasscode(
838
onCoreResult,
839
onFlowError,
840
context)
841
842
843
844
utils.log('logonCore.getContext');
845
onFlowError);
logonCore.getContext(onCoreResult,
846
847
848
849
onPasscodeEnable: ');
850
onFlowError, context);
//logonCore.changePasscode(onFlowSuccess,
851
onFlowError();
852
853
854
// exported properties
855
this.stateTransitions = [
856
857
condition: {
858
state: {
859
secureStoreOpen: true,
860
},
861
context: null
862
},
863
action: callGetContext
Kapsel Development
95
Kapsel Development
864
},
865
866
condition: {
867
state: {
868
secureStoreOpen: false,
869
870
},
871
action: onFlowError
872
},
873
874
condition: {
875
state: {
876
secureStoreOpen: true,
877
defaultPasscodeUsed: true,
878
//
defaultPasscodeAllowed: true,
879
880
},
881
action: 'SCR_MANAGE_PASSCODE_OPT_OFF'
882
},
883
884
condition: {
885
state: {
886
secureStoreOpen: true,
887
defaultPasscodeUsed: false,
888
defaultPasscodeAllowed: true,
889
96
890
},
891
action: 'SCR_MANAGE_PASSCODE_OPT_ON'
892
},
893
894
condition: {
895
state: {
Kapsel Development
896
secureStoreOpen: true,
897
//defaultPasscodeUsed: [DONTCARE],
898
defaultPasscodeAllowed: false,
899
900
},
901
action: 'SCR_MANAGE_PASSCODE_MANDATORY'
902
},
903
904
905
];
906
907
this.screenEvents = {
908
'SCR_MANAGE_PASSCODE_OPT_ON': {
909
onsubmit: onFlowSuccess,
910
oncancel: onFlowSuccess,
911
onerror: onFlowError,
912
ondisable:
showScreen('SCR_CHANGE_PASSCODE_OPT_OFF'),
913
onchange:
showScreen('SCR_CHANGE_PASSCODE_OPT_ON')
914
},
915
'SCR_MANAGE_PASSCODE_OPT_OFF': {
916
onsubmit: onFlowSuccess,
917
oncancel: onFlowSuccess,
918
onerror: onFlowError,
919
onenable:
showScreen('SCR_SET_PASSCODE_OPT_ON')
920
},
921
'SCR_MANAGE_PASSCODE_MANDATORY': {
922
onsubmit: onFlowSuccess,
923
oncancel: onFlowSuccess,
924
onerror: onFlowError,
925
onchange:
showScreen('SCR_CHANGE_PASSCODE_MANDATORY')
Kapsel Development
97
Kapsel Development
926
},
927
928
929
'SCR_SET_PASSCODE_OPT_ON': {
930
onsubmit: callChangePasscode,
931
oncancel: onFlowCancel,
932
onerror: onFlowError,
933
ondisable:
showScreen('SCR_SET_PASSCODE_OPT_OFF'),
934
onerrorack: noOp
935
},
936
'SCR_SET_PASSCODE_OPT_OFF': {
937
onsubmit: callDisablePasscode,
938
oncancel: onFlowCancel,
939
onerror: onFlowError,
940
onenable:
showScreen('SCR_SET_PASSCODE_OPT_ON'),
941
onerrorack: noOp
942
},
943
'SCR_CHANGE_PASSCODE_OPT_ON': {
944
onsubmit: callChangePasscode,
945
oncancel: onFlowCancel,
946
onerror: onFlowError,
947
ondisable:
showScreen('SCR_CHANGE_PASSCODE_OPT_OFF'),
948
onerrorack: noOp
949
},
950
'SCR_CHANGE_PASSCODE_OPT_OFF': {
951
onsubmit: callDisablePasscode,
952
oncancel: onFlowCancel,
953
onerror: onFlowError,
954
onenable:
showScreen('SCR_CHANGE_PASSCODE_OPT_ON'),
955
98
onerrorack: noOp
Kapsel Development
956
},
957
'SCR_CHANGE_PASSCODE_MANDATORY': {
958
onsubmit: callChangePasscode,
959
oncancel: onFlowCancel,
960
onerror: onFlowError,
961
onerrorack: noOp
962
},
963
964
};
965
966
967
968
969
970
var ShowRegistrationFlow = function
ShowRegistrationFlow(logonCore, logonView, onCoreResult,
onFlowSuccess, onFlowError, onFlowCancel) {
971
//wrapped into a function to defer evaluation of the
references to flow callbacks
972
973
this.name = 'showRegistrationFlowBuilder';
974
975
976
logonView.showScreen('SCR_SHOW_REGISTRATION',
this.screenEvents['SCR_SHOW_REGISTRATION'], context);
977
}.bind(this);
978
979
980
utils.log('logonCore.getContext');
981
onFlowError);
logonCore.getContext(onCoreResult,
982
983
984
Kapsel Development
// exported properties
99
Kapsel Development
985
this.stateTransitions = [
986
987
condition: {
988
state: {
989
secureStoreOpen: true,
990
991
},
992
context: null
993
},
994
action: callGetContext
995
},
996
997
condition: {
998
secureStoreOpen: true,
999
},
1000
action: showRegistrationInfo
1001
1002
1003
];
1004
1005
this.screenEvents = {
1006
'SCR_SHOW_REGISTRATION': {
1007
oncancel: onFlowSuccess,
1008
onerror: onFlowError
1009
1010
};
1011
1012
1013
1014
1015
100
Kapsel Development
1016
// === flow launcher methods
=====================================
1017
1018
1019
1020
1021
1022
'RESUME');
_oLogonCore.onEvent(onsuccess, onerror,
1023
1024
1025
1026
//call registration flow only if the status is
fullregistered in case of resume, so logon screen will not loose its
input values
1027
if (state.status == 'fullRegistered') {
1028
onerror);
registerOrUnlock(onUnlockSuccess,
1029
1030
1031
1032
1033
getState(onGetStateSuccess, onerror);
}
1034
1035
1036
1037
1038
1039
onerror, key);
_oLogonCore.getSecureStoreObject(onsuccess,
1040
1041
1042
1043
Kapsel Development
registerOrUnlock(onUnlockSuccess, onerror);
}
101
Kapsel Development
1044
1045
1046
1047
{
1048
1049
1050
onerror, key, value);
1051
_oLogonCore.setSecureStoreObject(onsuccess,
1052
1053
1054
registerOrUnlock(onUnlockSuccess, onerror);
}
1055
1056
1057
1058
1059
onerror);
1060
_oLogonCore.lockSecureStore(onsuccess,
}
1061
1062
1063
1064
_oLogonCore.getState(onsuccess, onerror);
}
1065
1066
1067
{
1068
var flowRunner = new FlowRunner(onsuccess, onerror,
_oLogonView, _oLogonCore);
1069
1070
flowRunner.run(RegistrationFlow);
}
1071
1072
102
Kapsel Development
1073
1074
1075
var innerFlowRunner = new FlowRunner(onsuccess,
onerror, _oLogonView, _oLogonCore);
1076
innerFlowRunner.run(ChangePasswordFlow);
1077
1078
1079
1080
registerOrUnlock(onUnlockSuccess, onerror);
}
1081
1082
1083
{
1084
1085
1086
var innerFlowRunner = new FlowRunner(onsuccess,
onerror, _oLogonView, _oLogonCore);
1087
innerFlowRunner.run(MockFlow);
1088
1089
1090
1091
registerOrUnlock(onUnlockSuccess, onerror);
}
1092
1093
1094
1095
1096
var innerFlowRunner = new FlowRunner(onsuccess,
onerror, _oLogonView, _oLogonCore);
1097
innerFlowRunner.run(ManagePasscodeFlow);
1098
1099
1100
1101
registerOrUnlock(onUnlockSuccess, onerror);
}
1102
Kapsel Development
103
Kapsel Development
1103
onerror) {
1104
1105
var innerFlowRunner = new FlowRunner(onsuccess,
onerror, _oLogonView, _oLogonCore);
1106
innerFlowRunner.run(ShowRegistrationFlow);
1107
1108
1109
1110
registerOrUnlock(onUnlockSuccess, onerror);
}
1111
1112
1113
utils.logJSON('LogonController.getSecureStoreErrorText: ' +
JSON.stringify(error));
1114
1115
var errorText;
1116
1117
if(error.errorCode === '14' && error.errorDomain
=== 'MAFSecureStoreManagerErrorDomain')
1118
errorText = "ERR_PASSCODE_TOO_SHORT";
1119
else if(error.errorCode === '10' &&
error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
1120
errorText = "ERR_PASSCODE_REQUIRES_DIGIT";
1121
else if(error.errorCode === '13' &&
error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
1122
errorText = "ERR_PASSCODE_REQUIRES_UPPER";
1123
else if(error.errorCode === '11' &&
error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
1124
errorText = "ERR_PASSCODE_REQUIRES_LOWER";
1125
else if(error.errorCode === '12' &&
error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
1126
errorText =
"ERR_PASSCODE_REQUIRES_SPECIAL";
1127
else if(error.errorCode === '15' &&
error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
104
Kapsel Development
1128
errorText =
"ERR_PASSCODE_UNDER_MIN_UNIQUE_CHARS";
1129
else {
1130
errorText = "ERR_SETPASSCODE_FAILED";
1131
1132
1133
1134
return errorText;
}
1135
1136
1137
utils.logJSON('LogonController.getSSOPasscodeSetErrorText: ' +
JSON.stringify(error));
1138
1139
var errorText;
1140
1141
if (error.errorDomain ===
'MAFLogonCoreErrorDomain') {
1142
1143
errorText =
"ERR_SSO_PASSCODE_SET_ERROR";
1144
1145
1146
1147
1148
return errorText;
}
1149
1150
1151
utils.logJSON('LogonController.getRegistrationErrorText: ' +
JSON.stringify(error));
1152
1153
var errorText;
1154
Kapsel Development
105
Kapsel Development
1155
if (error.errorDomain ===
'MAFLogonCoreErrorDomain') {
1156
1157
errorText =
"ERR_REG_FAILED_WRONG_SERVER";
1158
1159
1160
1161
errorText = "ERR_REG_FAILED";
1162
1163
1164
errorText =
"ERR_REG_FAILED_UNATHORIZED";
1165
1166
else {
1167
errorText = "ERR_REG_FAILED";
1168
1169
1170
1171
1172
return errorText;
}
1173
1174
1175
utils.logJSON('LogonController.getRegistrationCancelError: ' +
JSON.stringify(error));
1176
1177
var errorText;
1178
1179
errorText = "ERR_REGISTRATION_CANCEL";
1180
1181
1182
return errorText;
}
1183
106
Kapsel Development
1184
var errorWithDomainCodeDescription = function(domain,
code, description) {
1185
var error = {
1186
errorDomain: domain,
1187
errorCode: code,
1188
errorMessage: description
1189
};
1190
1191
return error;
1192
1193
1194
1195
1196
1197
// =================== exported (public) members
====================
1198
1199
/**
1200
* The Logon plugin provides screen flows to register an
app with an SAP Mobile Platform server.<br/>
1201
* <br/>
1202
* The logon plugin is a component of the SAP Mobile
Application Framework (MAF), exposed as a Cordova plugin. The basic
1203
* idea is that it provides screen flows where the user can
enter the values needed to connect to an SAP Mobile Platform 3.0
server and
1204
* stores those values in its own secure data vault. This
data vault is separate from the one provided with the
1205
* encrypted storage plugin. In an OData based SAP Mobile
Platform 3.0 application, a client must onboard or register with the
SAP Mobile Platform 3.0
1206
* server to receive an application connection ID for a
particular app. The application connection ID must be sent
1207
* along with each request that is proxied through the SAP
Mobile Platform 3.0 server to the OData producer.<br/>
1208
* <br/>
1209
Kapsel Development
107
Kapsel Development
1210
1211
* <a href="http://cordova.apache.org/docs/en/edge/
guide_cli_index.md.html#The%20Command-line%20Interface">Cordova
CLI</a>.<br/>
1212
* <br/>
1213
* To add the Logon plugin to your project, use the
following command:<br/>
1214
* cordova plugin add <full path to directory containing
Kapsel plugins>\logon<br/>
1215
* <br/>
1216
* To remove the Logon plugin from your project, use the
following command:<br/>
1217
1218
1219
* @namespace
1220
* @alias Logon
1221
* @memberof sap
1222
*/
1223
module.exports = {
1224
1225
/**
1226
* Initialization method to set up the Logon plugin.
will register the application with the SMP server and also
authenticate the user
This
1227
* with servers on the network. This step must be done
first prior to any attempt to communicate with the SMP server.
1228
1229
* @method
1230
* @param {sap.Logon~successCallback} successCallback The
function that is invoked if initialization is successful. The
current
1231
1232
* @param {sap.Logon~errorCallback} errorCallback The
function that is invoked in case of an error.
1233
* @param {string} applicationId The unique ID of the
application. Must match the application ID on the SAP Mobile
Platform server.
108
Kapsel Development
1234
* @param {object} [context] The context with default
values for application registration. See {@link
sap.Logon~successCallback} for the structure
1235
* of the context object. Note that all properties of the
context object are optional, and you only need to specify the
properties
1236
* for which you want to provide default values for.
values will be presented to the application users during the
registration process and given them
1237
The
1238
* @param {string} [logonView="com/sap/mp/logon/iabui"]
The cordova module ID of a custom renderer for the logon,
1239
* implementing the [showScreen(), close()] interface.
Please use the defaul module unless you are absolutely sure that you
can provide your own
1240
* custom implementation. Please refer to JavaScript
files inside your Kapsel project's plugins\logon\www\common\modules\
folder as example.
1241
* @example
1242
1243
1244
1245
* // The app ID
1246
1247
1248
* // You only need to specify the fields for which you
want to set the default.
These values are optional because they
will be
1249
screen.
1250
* var defaultContext = {
1251
1252
"https" : false,
1253
"serverPort" : "8080",
1254
"user" : "user1",
1255
"password" : "Zzzzzz123",
1256
"communicatorId" : "REST",
1257
"securityConfig" : "sec1",
Kapsel Development
"serverHost" : "defaultServerHost.com"
109
Kapsel Development
1258
"passcode" : "Aaaaaa123",
1259
"unlockPasscode" : "Aaaaaa123"
1260
* };
1261
1262
* var app_context;
1263
1264
1265
1266
* }
1267
1268
1269
1270
* }
app_context = context;
1271
* sap.Logon.init(successCallback, errorCallback,
applicationId, defaultContext, logonView);
1272
*/
1273
init: init,
1274
1275
1276
was called.
/**
* The application ID with which {@link sap.Logon.init}
It is available here so it is easy to access later.
1277
* @example
1278
1279
* alert("The app ID for this app is: " +
sap.Logon.applicationId);
1280
*/
1281
1282
applicationId: null,
/**
1283
* Direct reference to the logon core object used by the
Logon plugin. This is needed to perform more complex operations
that
1284
1285
* There are several functions that can be accessed on the
core object:<br/>
110
Kapsel Development
1286
*
getState(successCallback,errorCallback) returns the state object of
the application to the success callback in the form of a JavaScript
object.<br/>
1287
*
getContext(successCallback,errorCallback) returns the context object
of the application to the success callback in the form of a
JavaScript object.<br/>
1288
*
deleteRegistration(successCallback,errorCallback) deletes the
application's registration from the SAP Mobile Platform server and
removes<br/>
1289
*
&
nbsp; application data on device.<br/>
1290
* @example
1291
1292
1293
* }
1294
1295
1296
* }
1297
*
sap.Logon.core.getState(successCallback,errorCallback);
1298
*
sap.Logon.core.getContext(successCallback,errorCallback);
1299
*
sap.Logon.core.deleteRegistration(successCallback,errorCallback);
1300
*/
1301
1302
1303
/**
1304
* Get an
for a given key.
1305
* @method
1306
* @param {sap.Logon~getSuccessCallback} onsuccess The
function that is invoked
1307
* upon success.
a single parameter.
Kapsel Development
111
Kapsel Development
1308
* This can be null or undefined, if no object is defined
for the given key.
1309
* @param {sap.Logon~errorCallback} onerror The function
to invoke in case of error.
1310
DataVault.
1311
* @example
1312
1313
1314
* }
1315
1316
*
alert("value retrieved from the store: " +
JSON.stringify(value));
1317
* }
1318
1319
*
sap.Logon.get(getSuccess,errorCallback,'someKey');
1320
* }
1321
* sap.Logon.set(setSuccess,errorCallback,'someKey',
'some string (could also be an object).');
1322
*/
1323
get: get,
1324
1325
/**
1326
1327
* @method
1328
* @param {sap.Logon~successCallbackNoParameters}
onsuccess The function to invoke upon success.
1329
method.
1330
* @param {sap.Logon~errorCallback} onerror The function
to invoke in case of error.
1331
object on.
1332
key.
1333
112
Kapsel Development
1334
* @example
1335
1336
1337
* }
1338
1339
*
alert("value retrieved from the store: " +
JSON.stringify(value));
1340
* }
1341
1342
*
sap.Logon.get(getSuccess,errorCallback,'someKey');
1343
* }
1344
* sap.Logon.set(setSuccess,errorCallback,'someKey',
'some string (could also be an object).');
1345
*/
1346
set: set,
1347
1348
/**
1349
1350
* @method
1351
* @param {sap.Logon~successCallbackNoParameters}
onsuccess The function to invoke upon success.
1352
* @param {sap.Logon~errorCallback} onerror The function
to invoke in case of error.
1353
* @example
1354
1355
1356
* }
1357
1358
1359
* }
1360
* sap.Logon.lock(successCallback,errorCallback);
1361
*/
1362
Kapsel Development
alert("Locked!");
lock: lock,
113
Kapsel Development
1363
1364
/**
1365
* Unlock the Logon plugin's secure data vault if it has
been locked (due to being inactive, or
1366
* {@link sap.Logon.lock} being called), then the user is
prompted for the passcode to unlock the
1367
* application.<br/>
1368
* If the application is already unlocked, then nothing
will be done.<br/>
1369
* If the application has passcode disabled, then passcode
prompt will not be necessary.
1370
* In all cases if an error does not occur, the success
callback is invoked with the current logon context
1371
* as the parameter.
1372
* @method
1373
* @param {sap.Logon~successCallback} onsuccess - The
callback to call if the screen flow succeeds.
1374
* onsuccess will be called with the current logon context
as a single parameter.
1375
* @param {sap.Logon~errorCallback} onerror - The callback
to call if the screen flow fails.
1376
* @example
1377
1378
1379
* }
1380
1381
*
alert("Registered and unlocked.
JSON.stringify(context));
Context: " +
1382
* }
1383
* sap.Logon.unlock(successCallback,errorCallback);
1384
*/
1385
unlock: registerOrUnlock,
1386
1387
/**
1388
* This is an alias for registerOrUnlock.
function is equivalent
114
Calling this
Kapsel Development
1389
* to calling {@link sap.Logon.unlock} since both of them
are alias to registerOrUnlock.
1390
* @method
1391
* @private
1392
*/
1393
registerUser: registerOrUnlock,
1394
1395
/**
1396
* This function registers the user and creates a new
unlocked DataVault to store the registration
1397
* information.<br/>
1398
* If the user has already been registered, but the
application is locked (due to being inactive, or
1399
* {@link sap.Logon.lock} being called), then the user is
prompted for the passcode to unlock the
1400
* application.<br/>
1401
* If the application is already unlocked, then nothing
will be done.<br/>
1402
* In all cases if an error does not occur, the success
callback is invoked with the current logon context
1403
* as the parameter.
1404
* @method
1405
* @param {sap.Logon~successCallback} onsuccess - The
callback to call if the screen flow succeeds.
1406
* onsuccess will be called with the current logon context
as a single parameter.
1407
* @param {sap.Logon~errorCallback} onerror - The callback
to call if the screen flow fails.
1408
* @example
1409
1410
1411
* }
1412
1413
*
alert("Registered and unlocked.
JSON.stringify(context));
1414
Kapsel Development
Context: " +
* }
115
Kapsel Development
1415
*
sap.Logon.registerOrUnlock(successCallback,errorCallback);
1416
* @private
1417
*/
1418
registerOrUnlock: registerOrUnlock,
1419
1420
/**
1421
* This method will launch the UI screen for application
users to manage and update the data vault passcode or,
1422
* if the SMP server's Client Passcode Policy allows it,
enable or disable the passcode to the data vault.
1423
1424
* @method
1425
* @param {sap.Logon~successCallbackNoParameters}
onsuccess - The function to invoke upon success.
1426
* @param {sap.Logon~errorCallback} onerror - The function
to invoke in case of error.
1427
* @example
1428
1429
1430
* }
1431
1432
1433
* }
1434
*
sap.Logon.managePasscode(successCallback,errorCallback);
1435
*/
1436
managePasscode: managePasscode,
1437
1438
/**
1439
* This method will launch the UI screen for application
users to manage and update the back end passcode that Logon stores in
the
1440
* data vault that is used to authenticate the client to
the server.
1441
116
Kapsel Development
1442
* @method
1443
* @param {sap.Logon~successCallbackNoParameters}
onsuccess - The callback to call if the screen flow succeeds.
1444
method.
1445
* @param {sap.Logon~errorCallback} onerror The function
that is invoked in case of an error.
1446
* @example
1447
1448
1449
* }
1450
1451
1452
* }
1453
*
sap.Logon.changePassword(successCallback,errorCallback);
1454
*/
1455
changePassword: changePassword,
1456
1457
/**
1458
* Calling this method will show a UI screen with values
used for registrating application.
1459
* @method
1460
* @param {sap.Logon~successCallbackNoParameters}
onsuccess - The callback to call if the screen flow succeeds.
1461
method.
1462
* @param {sap.Logon~errorCallback} onerror The function
that is invoked in case of an error.
1463
* @example
1464
1465
1466
* }
1467
1468
*
successful.");
Kapsel Development
117
Kapsel Development
1469
* }
1470
*
sap.Logon.showRegistrationData(successCallback,errorCallback);
1471
*/
1472
showRegistrationData: showRegistrationData,
1473
1474
};
1475
1476
/**
1477
1478
1479
* @callback sap.Logon~errorCallback
1480
1481
* @param {Object} errorObject An object containing
properties: 'errorCode', 'errorMessage', and 'errorDomain'.
1482
error.
1483
* property is a string with more detailed information of what
went wrong. The 'errorDomain' property specifies
1484
1485
*/
1486
1487
/**
1488
* Callback function that is invoked upon successfully
registering or unlocking or retrieving the context.
1489
1490
* @callback sap.Logon~successCallback
1491
1492
* @param {Object} context An object containing the current
logon context. Two properties of particular importance
118
1493
1494
>
1495
* "registrationContext": {<br/>
Kapsel Development
1496
*
"serverHost": Host
of the server.<br/>
1497
* "domain":
Domain for server. Can be used in case of SAP Mobile Platform
communication.<br/>
1498
*
"resourcePath":
Resource path on the server. The path is used mainly for path based
reverse proxy but can contain a custom relay server path as well.<br/
>
1499
* "https":
Marks whether the server should be accessed in a secure way.<br/>
1500
*
"serverPort": Port
of the server.<br/>
1501
* "user":
Username in the backend.<br/>
1502
*
"password":
Password for the backend user.<br/>
1503
* "farmId":
FarmId of the server. Can be nil. Used in case of Relay server or
SiteMinder.<br/>
1504
*
"communicatorId":
Id of the communicator manager that will be used for performing the
logon. Possible values: IMO / GATEWAY / REST<br/>
1505
*
"securityConfig":
Security configuration. If nil, the default configuration is
used.<br/>
1506
*
"mobileUser":
Mobile User. Used in case of IMO manual user creation.<br/>
1507
*
"activationCode":
Activation Code. Used in case of IMO manual user creation.<br/>
1508
*
"gatewayClient":
The key string that identifies the client on the gateway. Used in
Gateway only registration mode. The value will be used as adding the
parameter: sap-client=<gateway client><br/>
1509
*
"gatewayPingPath":
Kapsel Development
119
Kapsel Development
The custom path of the ping URL on the gateway. Used in case of
Gateway only registration mode.<br/>
1510
* }<br/>
1511
* "applicationEndpointURL": Contains the application
endpoint URL after a successful registration.<br/>
1512
* "applicationConnectionId": ID to get after a successful
SUP REST registration. Needs to be set in the download request header
with key X-SUP-APPCID<br/>
1513
>
1514
* "policyContext": Contains the password policy for the
secure store {<br/>
1515
*
"alwaysOn":<br/>
1516
*
"alwaysOff":<br/>
1517
*
"defaultOn":<br/>
1518
*
"hasDigits":<br/>
1519
*
"hasLowerCaseLetter
s":<br/>
1520
*
"hasSpecialLetters"
:<br/>
1521
*
"hasUpperCaseLetter
s":<br/>
1522
*
"defaultAllowed":<b
r/>
1523
*
"expirationDays":<b
r/>
1524
*
"lockTimeout":<br/>
1525
*
"minLength":<br/>
1526
*
"minUniqueChars":<b
r/>
120
Kapsel Development
1527
*
"retryLimit":<br/>
1528
* }<br/>
1529
* "registrationReadOnly": specifies whether context values
are coming from clientHub / afaria<br/>
1530
* "policyReadOnly": specifies whether passcode policy is
coming from afaria<br/>
1531
* "credentialsByClientHub": specifies whether credentials
are coming from clientHub
1532
*/
1533
1534
/**
1535
* Callback function that will be invoked with no
parameters.
1536
1537
* @callback sap.Logon~successCallbackNoParameters
1538
*/
1539
1540
/**
1541
* Callback function that is invoked upon successfully
retrieving an object from the DataVault.
1542
1543
* @callback sap.Logon~getSuccessCallback
1544
1545
* @param {Object} value The object that was stored with the
given key. Can be null or undefined if no object was stored
1546
1547
*/
1548
1549
Kapsel Development
121
Kapsel Development
get = function (url, header, successCB, errorCB, user, password, timeout, certSource).
This is a convenience function and provides no additional functionality compared to the
sendRequest function. It just calls the sendRequest function with the method set to
GET and no requestBody.
sendRequest = function (method, url, header, requestBody, successCB, errorCB, user,
password, timeout, certSource).
Constructor Functions
There are three constructor functions to make objects that you can use for certificates:
Note: The success callback is called upon any response from the server, so be sure to check
the status on the response.
Adding the AuthProxy Plugin
Use the Cordova command line interface to install the AuthProxy plugin.
Prerequisites
122
Kapsel Development
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
On iOS:
The plugin depends on afariaSLL.a
Requires the link flag of "-lstdc++," if not yet included.
Task
1. Add the AuthProxy plugin by entering the following at the command prompt, or terminal:
On Windows:
cordova -d plugin add <SDK_HOME>\MobileSDK3\KapselSDK
\plugins\authproxy
On Mac:
cordova -d plugin add ~<SDK_HOME>/MobileSDK3/KapselSDK/
plugins/authproxy
Note: The path you enter to the Kapsel plugin must be the absolute path (not relative
path).
2. (Optional) To see a list of installed plugins in your Cordova project, open a command
prompt or terminal window, navigate to your Cordova project folder, and enter:
cordova plugins
The Cordova command line interface returns a JSON array showing installed plugins, for
example:
[ 'org.apache.cordova.core.camera',
'org.apache.cordova.core.device-motion',
'org.apache.cordova.core.file' ]
In this example, the Cordova project has the Cordova core Camera, Accelerator (devicemotion), and File plugins installed.
3. Modify the files in the www folder for the project as necessary, then copy them to the
platform directories by running:
cordova -d prepare android
cordova -d prepare ios
4. Use the Android IDE or Xcode to deploy and run the project.
5. (Optional) For iOS, if the application uses Afaria mutual certificate authentication, or if
multiple applications on the devices need to share the credentials, you must first build and
deploy Client Hub to the device, and then add the "clienthubEntitlements" and "$
(CFBundleIdentifier)" items to the shared keychain groups in the application's project
settings in Xcode.
Kapsel Development
123
Kapsel Development
124
Kapsel Development
var failureCallback = function( error ) {
alert("Error! Code: " + error.errorCode + "\n" +
error.description + "\nNative error code: " +
error.nativeErrorCode );
}
// setting a cookie with a request
var header = {cookie:
"customCookieName=customCookieValue;anotherName=AnotherValue"};
sap.AuthProxy.sendRequest("POST", "http://www.example.com/stuff/
etc", header, null, successCallback, failureCallback);
Using the AuthProxy Plugin to Register With SAP Mobile Platform Server
This example procedure demonstrates how to use the AuthProxy plugin to register with the
SAP Mobile Platform Server using a client certificate.
This example does not use the Logon plugin to perform the registration. You can test
certificates on an Android device or emulator, or an iOS device. The server certificate must be
installed on the device's system store, so for iOS, the actual device is required.
1. Use the keytool utility to create the server and client certificates.
The SAP Mobile Platform Server stores its certificates in a file named
smp_keystore.jks.
2. Download the certificate and generate a certificate signing request (CSR).
3. Import the signed certificate into the keystore.
4. Copy the client's public key to smp_keystore.jks so that the server can authenticate
the client.
5. Create a security profile in Management Cockpit
6. Import the public and private key of the client certificate to the mobile device using the
PKCS12 format.
Both the client certificate (stored in the keystore client.p12 containing the public and
private keys) and the certificate authority's certificate, must be added to the mobile device.
You should add the certificate authority's certificate to the device's trust store. The client
certificate in this example for Android is placed in a location the application can access it
from.
adb push SAPServerCA.cer /mnt/sdcard/
adb push client.p12 /mnt/sdcard/
adb shell
cd /mnt/sdcard
ls
exit
For an iOS device, both certificates can be installed into the device's trusted store by
sending them through an e-mail, opening the device browser to a Web page that contains
the links to the certificates, or by using the iPhone Configuration Utility. See http://
support.apple.com/kb/DL1465.
Kapsel Development
125
Kapsel Development
On the iOS device, the certificates can be viewed and uninstalled under Settings >
General > Profiles.
In addition to accessing the certificate from the file system and the device's secure store,
the client certificate can be provisioned to the device using Afaria and then accessed from
Afaria using the Logon plugin using the method
sap.AuthProxy.CertificateFromLogonManager("clientKey").
7. Create a new Cordova project to perform mutual authentication to the SAP Mobile
Platform Server.
8. Add the AuthProxy plugin.
9. Create a new security provider and add an x.509 User Certificate authentication provider.
10. Copy the files to the platform directory by running the prepare command.
11. Use the Android IDE or Xcode to deploy and run the project.
Generating Certificates and Keys
Use a PKI system and a trusted CA to generate production-ready certificates and keys that
encrypt communication among different SAP Mobile Platform components. You can then use
the keytool utility to import and export certificate to the keystore.
Note: Any changes to the keystore require the server to be restarted.
Kapsel AuthProxy API Reference
The Kapsel AuthProxy API Reference provides usage information for AuthProxy API classes
and methods, as well as provides sample source code.
AuthProxy namespace
The AuthProxy plugin provides the ability to make HTTPS requests with mutual
authentication.
The regular XMLHttpRequest does not support mutual authentication. The AuthProxy plugin
allows you to specify a certificate to include in an HTTPS request to identify the client to the
server. This allows the server to verify the identity of the client. An example of where you
might need mutual authenticaion is the onboarding process to register with an application, or,
to access an OData producer. This occurs mostly in Business to Business (B2B) applications.
This is different from most business to consumer (B2C) web sites where it is only the server
that authenticates itself to the client with a certificate.
126
Kapsel Development
The AuthProxy plugin is added and removed using the Cordova CLI.
To add the AuthProxy plugin to your project, use the following command:
cordova plugin add <path to directory containing Kapsel plugins>\authproxy
To remove the AuthProxy plugin from your project, use the following command:
cordova plugin rm com.sap.mp.cordova.plugins.authproxy
Classes
Name
Description
sap.AuthProxy.CertificateFromFile on page
129
sap.AuthProxy.CertificateFromLogonManager
on page 131
sap.AuthProxy.CertificateFromStore on page
132
Members
Name
Description
ERR_CERTIFICATE_ALIAS_NOT_FOUND
on page 132
ERR_CERTIFICATE_FILE_NOT_EXIST on
page 133
ERR_CLIENT_CERTIFICATE_VALIDATION
on page 133
Kapsel Development
127
Kapsel Development
ERR_GET_CERTIFICATE_FAILED on page
134
ERR_INVALID_PARAMETER_VALUE on
page 134
ERR_LOGON_MANAGER_CERTIFICATE_METHOD_NOT_AVAILABLE on page
134
Constant indicating there is no such Cordova action for the current service.
ERR_SYSTEM_CERTIFICATE_SOURCE_UNSUPPORTED on page
136
Constant indicating the certificate from the system keystore is not supported on the current platform.
Methods
128
Name
Description
Generates an OData client that uses the AuthProxy plugin to make requests.
Kapsel Development
sendRequest( method, url, header, requestBody,
successCB, errorCB, [user], [password], [timeout], [certSource] ) on page 141
Type Definitions
Name
Description
Callback function that is invoked upon successfully deleting a certificate from the store.
Source
Kapsel Development
Type
Description
129
Kapsel Development
Path
string
Password
string
CertificateKey
string
Example
// Create the certificate source description object.
var fileCert = new sap.AuthProxy.CertificateFromFile("directory/
certificateName.p12", "certificatePassword", "certificateKey");
// callbacks
var successCB = function(serverResponse){
alert("Status: " + JSON.stringify(serverResponse.status));
alert("Headers: " + JSON.stringify(serverResponse.headers));
alert("Response: " + JSON.stringify(serverResponse.response));
}
var errorCB = function(errorObject){
alert("Error making request: " + JSON.stringify(errorObject));
}
// Make the request with the certificate source description object.
sap.AuthProxy.sendRequest("POST", "https://hostname", headers, "THIS
IS THE BODY", successCB, errorCB, null, null, 0, fileCert);
130
Kapsel Development
Source
Type
Description
appID
string
application identifier
Example
// Create the certificate source description object.
var logonCert = new
sap.AuthProxy.CertificateFromLogonManager("applicationID");
// callbacks
var successCB = function(serverResponse){
alert("Status: " + JSON.stringify(serverResponse.status));
alert("Headers: " + JSON.stringify(serverResponse.headers));
alert("Response: " + JSON.stringify(serverResponse.response));
}
var errorCB = function(errorObject){
alert("Error making request: " + JSON.stringify(errorObject));
}
// Make the request with the certificate source description object.
sap.AuthProxy.sendRequest("POST", "https://hostname", headers, "THIS
IS THE BODY", successCB, errorCB, null, null, 0, logonCert);
Source
authproxy.js, line 281 on page 156.
Kapsel Development
131
Kapsel Development
sap.AuthProxy.CertificateFromStore class
Create a certificate source description object for certificates from the system keystore.
You might want to use a certificate from the system keystore if you know the user's device will
have the desired certificate installed on it.
On Android, sending a request with a certificate from the system store results in UI being
shown for the user to pick the certificate to use (the certificate with the alias matching the given
CertificateKey is pre-selected).
Syntax
new CertificateFromStore( CertificateKey )
Parameters
Name
Type
Description
CertificateKey
string
Example
// Create the certificate source description object.
var systemCert = new
sap.AuthProxy.CertificateFromStore("certificatekey");
// callbacks
var successCB = function(serverResponse){
alert("Status: " + JSON.stringify(serverResponse.status));
alert("Headers: " + JSON.stringify(serverResponse.headers));
alert("Response: " + JSON.stringify(serverResponse.response));
}
var errorCB = function(errorObject){
alert("Error making request: " + JSON.stringify(errorObject));
}
// Make the request with the certificate source description object.
sap.AuthProxy.sendRequest("POST", "https://hostname", headers, "THIS
IS THE BODY", successCB, errorCB, null, null, 0, systemCert);
Source
authproxy.js, line 253 on page 155.
ERR_CERTIFICATE_ALIAS_NOT_FOUND member
Constant indicating the certificate with the given alias could not be found.
Used as a possible value for the errorCode in sap.AuthProxy~errorCallback on page 143.
Syntax
<constant> ERR_CERTIFICATE_ALIAS_NOT_FOUND : number
132
Kapsel Development
Source
Kapsel Development
133
Kapsel Development
Source
134
Kapsel Development
Source
Kapsel Development
135
Kapsel Development
ERR_SERVER_CERTIFICATE_VALIDATION member
Constant indicating the server certificate failed validation on the client side.
This is likely because the server certificate is self-signed, or not signed by a well-known
certificate authority. This constant is used as a possible value for the errorCode in
sap.AuthProxy~errorCallback on page 143.
Syntax
<constant> ERR_SERVER_CERTIFICATE_VALIDATION : number
Source
136
Kapsel Development
Source
Type
successCB
errorCB
certificateKey
string
Argument
Description
Callback method upon
success.
(optional)
Example
var successCB = function(){
alert("certificate successfully deleted.");
}
var errorCB = function(error){
alert("error deleting certificate: " + JSON.stringify(error));
}
sap.AuthProxy.deleteCertificateFromStore(successCB, errorCB,
"certificateKeyToDelete");
Source
Kapsel Development
137
Kapsel Development
generateODataHttpClient() method
Generates an OData client that uses the AuthProxy plugin to make requests.
This is useful if you are using Datajs, but want to make use of the certificate features of
AuthProxy. Datajs is a javascript library useful for accessing OData services. Datajs has a
concept of an HttpClient, which does the work of making the request. This function generates
an HttpClient that you can specify to Datajs so you can provide client certificates for requests.
If you want to use the generated HTTP client for all future Datajs requests, you can do that by
setting the OData.defaultHttpClient property to the return value of this function. Once that is
done, then doing OData stuff with Datajs is almost exactly the same, but you can add a
certificateSource to a request.
Syntax
generateODataHttpClient()
Example
OData.defaultHttpClient = sap.AuthProxy.generateODataHttpClient();
// Using a certificate from file, for example.
fileCert = new sap.AuthProxy.CertificateFromFile("mnt/sdcard/
cert.p12", "password", "certKey");
// This is the same request object you would have created if you were
just using Datajs, but now
// you can add the extra 'certificateSource' property.
var createRequest = {
requestUri: "http://www.example.com/stuff/etc/example.svc",
certificateSource : fileCert,
user : "username",
password : "password",
method : "POST",
data:
{
Description: "Created Record",
CategoryName: "Created Category"
}
}
// Use Datajs to send the request.
OData.request( createRequest, successCallback, failureCallback );
Source
authproxy.js, line 733 on page 173.
138
Kapsel Development
Type
url
string
header
Object
successCB
sap.AuthProxy~successCallback on page
144
errorCB
user
string
(optional)
password
string
(optional)
timeout
number
(optional)
Kapsel Development
Argument
Description
139
Kapsel Development
certSource
Object
(optional)
Certificate description
object.It can be one of
sap.AuthProxy#CertificateFromFile on page
129, sap.AuthProxy#CertificateFromStore on page
132, or sap.AuthProxy#CertificateFromLogonManager
on page 131.
Returns
A JavaScript function object to abort the operation. Calling the abort function results in neither
the success or error callback being invoked for the original request (excepting the case where
the success or error callback was invoked before calling the abort functino). Note that the
request itself cannot be unsent, and the server will still receive the request - the JavaScript will
just not know the results of that request.
Type:
function
Example
var successCB = function(serverResponse){
alert("Status: " + JSON.stringify(serverResponse.status));
alert("Headers: " + JSON.stringify(serverResponse.headers));
if (serverResponse.responseText){
alert("Response: " +
JSON.stringify(serverResponse.responseText));
}
}
var errorCB = function(errorObject){
alert("Error making request: " + JSON.stringify(errorObject));
}
// To send a GET request to server, call the method
var abortFunction = sap.AuthProxy.get("http://www.example.com",
null, successCB, errorCB);
// An example of aborting the request
abortFunction();
// To send a GET request to the server with headers, call the method
sap.AuthProxy.get("http://www.example.com", {HeaderName : "Header
value"}, successCB, errorCB);
// To send a GET request to the server with basic authentication,
call the method
sap.AuthProxy.get("https://www.example.com", headers, successCB,
errorCB, "username", "password");
// To send a GET request to the server with mutual authentication,
call the method
140
Kapsel Development
sap.AuthProxy.get("https://www.example.com", headers, successCB,
errorCB, null, null, 0,
new sap.AuthProxy.CertificateFromLogonManager("theAppId"));
Source
Type
method
string
url
string
header
Object
requestBody
string
successCB
sap.AuthProxy~successCallback on page
144
Kapsel Development
Argument
Description
141
Kapsel Development
errorCB
user
string
(optional)
password
string
(optional)
timeout
number
(optional)
certSource
Object
(optional)
Certificate description
object.It can be one of
sap.AuthProxy#CertificateFromFile on page
129, sap.AuthProxy#CertificateFromStore on page
132, or sap.AuthProxy#CertificateFromLogonManager
on page 131.
Returns
A JavaScript function object to abort the operation. Calling the abort function results in neither
the success or error callback being invoked for the original request (excepting the case where
the success or error callback was invoked before calling the abort function). Note that the
request itself cannot be unsent, and the server will still receive the request - the JavaScript will
just not know the results of that request.
Type:
function
Example
// callbacks
var successCB = function(serverResponse){
alert("Status: " + JSON.stringify(serverResponse.status));
alert("Headers: " + JSON.stringify(serverResponse.headers));
alert("Response: " + JSON.stringify(serverResponse.response));
}
var errorCB = function(errorObject){
alert("Error making request: " + JSON.stringify(errorObject));
}
// To send a post request to the server, call the method
142
Kapsel Development
var abortFunction = sap.AuthProxy.sendRequest("POST", "http://
www.google.com", null, "THIS IS THE BODY", successCB, errorCB);
// An example of aborting the request
abortFunction();
// To send a post request to the server with headers, call the method
sap.AuthProxy.sendRequest("POST", url, {HeaderName : "Header
value"}, "THIS IS THE BODY", successCB, errorCB);
// To send a post request to the server with basic authentication,
call the method
sap.AuthProxy.sendRequest("POST", url, headers, "THIS IS THE BODY",
successCB, errorCB, "username", "password");
// To send a post request to the server with mutual authentication,
call the method
sap.AuthProxy.sendRequest("POST", "https://hostname", headers, "THIS
IS THE BODY", successCB, errorCB, null,
null, 0, new
sap.AuthProxy.CertificateFromLogonManager("theAppId"));
Source
authproxy.js, line 466 on page 163.
deleteCertificateSuccessCallback type
Callback function that is invoked upon successfully deleting a certificate from the store.
Syntax
deleteCertificateSuccessCallback()
Source
Kapsel Development
Type
Description
143
Kapsel Development
errorObject
Object
An object containing two properties: 'errorCode' and 'description.' The 'errorCode' property
corresponds to one of the
sap.AuthProxy on page 126
constants. The 'description'
property is a string with more
detailed information of what
went wrong.
Example
function errorCallback(errCode) {
//Set the default error message. Used if an invalid code is passed
to the
//function (just in case) but also to cover the
//sap.AuthProxy.ERR_UNKNOWN case as well.
var msg = "Unkown Error";
switch (errCode) {
case sap.AuthProxy.ERR_INVALID_PARAMETER_VALUE:
msg = "Invalid parameter passed to method";
break;
case sap.AuthProxy.ERR_MISSING_PARAMETER:
msg = "A required parameter was missing";
break;
case sap.AuthProxy.ERR_HTTP_TIMEOUT:
msg = "The request timed out";
break;
};
//Write the error to the log
console.error(msg);
//Let the user know what happened
navigator.notification.alert(msg, null, "AuthProxy Error", "OK");
};
Source
144
Type
Description
Kapsel Development
serverResponse
Object
Source
// ${project.version}
3
4
/**
5
* The AuthProxy plugin provides the ability to make HTTPS
requests with mutual authentication.<br/>
6
* <br/>
Kapsel Development
145
Kapsel Development
8
* support mutual authentication. The AuthProxy plugin allows
you to specify a certificate to include in an HTTPS request
9
* to identify the client to the server. This allows the
server to verify the identity of the client. An example of where
you
10
* might need mutual authentication is the onboarding process
to register with an application, or, to access an
11
* OData producer. This occurs mostly in Business to Business
(B2B) applications. This is different from most business to
12
* consumer (B2C) Web sites, where it is only the server that
authenticates itself to the client with a certificate.<br/>
13
* <br/>
14
15
16
* <a href="http://cordova.apache.org/docs/en/edge/
guide_cli_index.md.html#The%20Command-line%20Interface">Cordova
CLI</a>.<br/>
17
* <br/>
18
* To add the AuthProxy plugin to your project, use the
following command:<br/>
19
* cordova plugin add <path to directory containing Kapsel
plugins>\authproxy<br/>
20
* <br/>
21
* To remove the AuthProxy plugin from your project, use the
following command:<br/>
22
23
* @namespace
24
* @alias AuthProxy
25
* @memberof sap
26
*/
27
28
29
30
/**
31
32
*/
33
146
Kapsel Development
34
/**
35
* Constant indicating the operation failed with unknown
error. Used as a possible value for the
36
37
* @constant
38
* @type number
39
*/
40
AuthProxy.prototype.ERR_UNKNOWN = -1;
41
42
/**
43
* Constant indicating the operation failed due to an invalid
parameter (for example, a string was passed where a number was
44
* required). Used as a possible value for the errorCode in
{@link sap.AuthProxy~errorCallback}.
45
* @constant
46
* @type number
47
*/
48
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE = -2;
49
50
/**
51
* Constant indicating the operation failed because of a
missing parameter. Used as a possible value for the
52
53
* @constant
54
* @type number
55
*/
56
AuthProxy.prototype.ERR_MISSING_PARAMETER = -3;
57
58
/**
59
* Constant indicating there is no such Cordova action for the
current service. When a Cordova plugin calls into native
60
* code it specifies an action to perform. If the action
provided by the JavaScript is unknown to the native code this
61
* error occurs. This error should not occur as long as
authproxy.js is unmodified. Used as a possible
Kapsel Development
147
Kapsel Development
62
* value for the errorCode in {@link
sap.AuthProxy~errorCallback}.
63
* @constant
64
* @type number
65
*/
66
AuthProxy.prototype.ERR_NO_SUCH_ACTION = -100;
67
68
/**
69
* Constant indicating the certificate from file is not
supported on the current platform. Used as a possible value for the
70
71
* @constant
72
* @type number
73
*/
74
AuthProxy.prototype.ERR_FILE_CERTIFICATE_SOURCE_UNSUPPORTED
= -101;
75
76
/**
77
* Constant indicating the certificate from the system
keystore is not supported on the current platform. Used as a possible
value
78
* for the errorCode in {@link
sap.AuthProxy~errorCallback}.
79
* @constant
80
* @type number
81
*/
82
AuthProxy.prototype.ERR_SYSTEM_CERTIFICATE_SOURCE_UNSUPPORTED =
-102;
83
84
/**
85
* Constant indicating the certificate with the given alias
could not be found. Used as a possible value for the
148
86
87
* @constant
88
* @type number
Kapsel Development
89
90
*/
AuthProxy.prototype.ERR_CERTIFICATE_ALIAS_NOT_FOUND = -104;
91
92
/**
93
* Constant indicating the certificate file could not be
found. Used as a possible value for the
94
95
* @constant
96
* @type number
97
*/
98
AuthProxy.prototype.ERR_CERTIFICATE_FILE_NOT_EXIST = -105;
99
100
/**
101
* Constant indicating incorrect certificate file format.
Used as a possible value for the
102
103
* @constant
104
* @type number
105
*/
106
-106;
AuthProxy.prototype.ERR_CERTIFICATE_INVALID_FILE_FORMAT =
107
108
/**
109
* Constant indicating failure in getting the certificate.
Used as a possible value for the
110
111
* @constant
112
* @type number
113
*/
114
AuthProxy.prototype.ERR_GET_CERTIFICATE_FAILED = -107;
115
116
/**
117
* Constant indicating the provided certificate failed
validation on the server side. Used as a possible value for the
Kapsel Development
149
Kapsel Development
118
119
* @constant
120
* @type number
121
*/
122
-108;
AuthProxy.prototype.ERR_CLIENT_CERTIFICATE_VALIDATION =
123
124
/**
125
* Constant indicating the server certificate failed
validation on the client side. This is likely because the server
certificate
126
* is self-signed, or not signed by a well-known certificate
authority. This constant is used as a possible value for the
127
128
* @constant
129
* @type number
130
*/
131
-109;
AuthProxy.prototype.ERR_SERVER_CERTIFICATE_VALIDATION =
132
133
/**
134
* Constant indicating the server request failed. Used as a
possible value for the
135
136
* @constant
137
* @type number
138
*/
139
AuthProxy.prototype.ERR_SERVER_REQUEST_FAILED = -110;
140
141
/**
142
* Constant indicating the Logon Manager core library is not
available. Getting this error code means you tried
143
* to use Logon plugin features (for example, a certificate
from Logon) without adding the Logon plugin to the app.
144
* A possible value for the errorCode in {@link
sap.AuthProxy~errorCallback}.
150
Kapsel Development
145
* @constant
146
* @type number
147
*/
148
-111;
AuthProxy.prototype.ERR_LOGON_MANAGER_CORE_NOT_AVAILABLE =
149
150
/**
151
* Constant indicating the Logon Manager certifciate method is
not available. Used as a possible value for the
152
153
* @constant
154
* @type number
155
*/
156
AuthProxy.prototype.ERR_LOGON_MANAGER_CERTIFICATE_METHOD_NOT_AVAILA
BLE = -112;
157
158
/**
159
* Constant indicating timeout error while connecting to the
server. Used as a possible value for the
160
161
* @constant
162
* @type number
163
*/
164
AuthProxy.prototype.ERR_HTTP_TIMEOUT = -120;
165
166
/**
167
* Constant indicating timeout error while connecting to the
server. Used as a possible value for the
168
169
* @constant
170
* @type number
171
*/
172
Kapsel Development
151
Kapsel Development
173
/**
174
* Constant indicating a missing required parameter message.
Used as a possible value for the description
175
* in (@link sap.AuthProxy~errorCallback}.
176
* @constant
177
* @type string
178
* @private
179
*/
180
AuthProxy.prototype.MSG_MISSING_PARAMETER = "Missing a
required parameter: ";
181
182
/**
183
* Constant indicating invalid parameter value message.
as a possible value for the description
184
* in (@link sap.AuthProxy~errorCallback}.
185
* @constant
186
* @type string
187
* @private
188
*/
Used
189
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE = "Invalid
Parameter Value for parameter: ";
190
191
/**
192
* Create certificate source description object for a
certificate from a keystore file. The keystore file must be of type
PKCS12
193
* (usually a .p12 extension) since that is the only
certificate file type that can contain a private key (a private key
is needed
194
* to authenticate the client to the server). You might want
to use this method if you know the desired certificate resides in a
195
196
* @class
197
* @param {string} Path The Path of the keystore file.<br/>For
iOS clients, it first tries to load the
198
*
relative file path from the application's
Documents folder. If it fails, it then tries
152
Kapsel Development
199
*
to load the file path from the application's
main bundle. In addition, before trying
200
*
to load the certificate from the file system,
the iOS client first checks whether the
201
*
specified certificate key already exists in
the key store. If it does, it loads
202
*
the existing certificate from the key store,
instead of loading the certificate from the
203
file system.<br/>
204
*
For Android clients, the file path is first
treated as an absolute path. If the certificate
205
*
is not found, the file path is treated as
relative to the root of the SD card.
206
207
* @param {string} CertificateKey A unique key (alias) that is
used to locate the certificate.
208
* @example
209
210
* var fileCert = new
sap.AuthProxy.CertificateFromFile("directory/certificateName.p12",
"certificatePassword", "certificateKey");
211
* // callbacks
212
213
*
alert("Status: " +
JSON.stringify(serverResponse.status));
214
*
alert("Headers: " +
JSON.stringify(serverResponse.headers));
215
*
alert("Response: " +
JSON.stringify(serverResponse.response));
216
* }
217
218
*
alert("Error making request: " +
JSON.stringify(errorObject));
219
* }
220
* // Make the request with the certificate source description
object.
221
* sap.AuthProxy.sendRequest("POST", "https://hostname",
headers, "THIS IS THE BODY", successCB, errorCB, null, null, 0,
fileCert);
Kapsel Development
153
Kapsel Development
222
223
*/
224
AuthProxy.prototype.CertificateFromFile = function (Path,
Password, CertificateKey) {
225
this.Source = "FILE";
226
this.Path = Path;
227
this.Password = Password;
228
this.CertificateKey = CertificateKey;
229
};
230
231
/**
232
* Create a certificate source description object for
certificates from the system keystore. You might want to use a
certificate
233
* from the system keystore if you know the user's device will
have the desired certificate installed on it.<br/>
234
* On Android, sending a request with a certificate from the
system store results in UI being shown, where the user can pick
235
* the certificate to use (the certificate with the alias
matching the given CertificateKey is pre-selected).
236
* @class
237
* @param {string} CertificateKey A unique key (alias) that is
used to locate the certificate.
238
* @example
239
240
* var systemCert = new
sap.AuthProxy.CertificateFromStore("certificatekey");
241
* // callbacks
242
243
*
alert("Status: " +
JSON.stringify(serverResponse.status));
244
*
alert("Headers: " +
JSON.stringify(serverResponse.headers));
245
*
alert("Response: " +
JSON.stringify(serverResponse.response));
154
246
* }
247
Kapsel Development
248
*
alert("Error making request: " +
JSON.stringify(errorObject));
249
* }
250
* // Make the request with the certificate source description
object.
251
* sap.AuthProxy.sendRequest("POST", "https://hostname",
headers, "THIS IS THE BODY", successCB, errorCB, null, null, 0,
systemCert);
252
*/
253
AuthProxy.prototype.CertificateFromStore = function
(CertificateKey) {
254
this.Source = "SYSTEM";
255
this.CertificateKey = CertificateKey;
256
};
257
258
259
/**
260
* Create a certificate source description object for
certificates from Logon Manager. Using the resulting certificate
source description
261
* object on subsequent calls to AuthProxy.sendRequest or
AuthProxy.get causes AuthProxy to retrieve a certificate from Logon
Manager
262
* to use for client authentication. The appID parameter is
used to indicate which application's certificate to use.<br/>
263
* Note: To use a certificate from Logon Manager, the
application must have already registered with the server using a
certificate from Afaria.
264
* @class
265
266
* @example
267
268
* var logonCert = new
sap.AuthProxy.CertificateFromLogonManager("applicationID");
269
* // callbacks
270
271
*
alert("Status: " +
JSON.stringify(serverResponse.status));
Kapsel Development
155
Kapsel Development
272
*
alert("Headers: " +
JSON.stringify(serverResponse.headers));
273
*
alert("Response: " +
JSON.stringify(serverResponse.response));
274
* }
275
276
*
alert("Error making request: " +
JSON.stringify(errorObject));
277
* }
278
* // Make the request with the certificate source description
object.
279
* sap.AuthProxy.sendRequest("POST", "https://hostname",
headers, "THIS IS THE BODY", successCB, errorCB, null, null, 0,
logonCert);
280
*/
281
AuthProxy.prototype.CertificateFromLogonManager = function
(appID) {
282
this.Source = "LOGON";
283
this.AppID = appID;
284
};
285
286
287
/**
288
* Verifies that a certificate source description object
(created with {@link sap.AuthProxy#CertificateFromFile},
289
* {@link sap.AuthProxy#CertificateFromStore}, or {@link
sap.AuthProxy#CertificateFromLogonManager}) has all the required
fields and that the values
290
* for those fields are the correct type. This function
verifies only the certificate description object, not the certificate
itself. So, for example,
291
* if the certificate source description object was created
with {@link sap.AuthProxy#CertificateFromFile} and has a string for
the file path and a
292
* string for the key/alias, <b>this function considers it
valid even if no certificate actually exists on the file system</b>.
If the certificate
293
* source description object is valid but the certificate
itself is not, then an error occurs during the call to {@link
sap.AuthProxy#get} or
156
Kapsel Development
294
* {@link sap.AuthProxy#sendRequest}.
295
object.
296
* @param {sap.AuthProxy~errorCallback} errorCB The error
callback invoked if the certificate source is not valid. Will have
an object with 'errorCode'
297
298
* @example
299
300
301
*
alert("certificate not valid!\nError code: " +
error.errorCode + "\ndescription: " + error.description);
302
* }
303
* var isCertValid =
sap.AuthProxy.validateCertSource(notValidCert, errorCallback);
304
* if( isCertValid ){
305
*
// do stuff with the valid certificate source
description object
306
* } else {
307
*
// at this point we know the cert is not valid, and the
error callback is invoked with extra information.
308
* }
309
310
311
312
* @private
313
*/
314
AuthProxy.prototype.validateCertSource = function
(certSource, errorCB) {
315
if (!certSource) {
316
it.
317
return true;
318
319
320
// errorCB required.
Kapsel Development
157
Kapsel Development
321
errors
322
323
function");
324
return false;
325
326
327
try {
328
329
330
errorCB({
331
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
332
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "certSource"
333
});
334
return false;
335
336
337
338
339
errorCB({
340
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
341
description:
AuthProxy.prototype.MSG_MISSING_PARAMETER + "keystore path"
342
});
343
return false;
344
345
346
347
348
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
158
Kapsel Development
349
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "keystore path"
350
});
351
return false;
352
353
354
if (!certSource.CertificateKey) {
355
errorCB({
356
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
357
description:
AuthProxy.prototype.MSG_MISSING_PARAMETER + "certificate key"
358
});
359
return false;
360
361
362
{
363
errorCB({
364
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
365
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "certificate key"
366
});
367
return false;
368
369
370
371
}
} else if (certSource.Source === "SYSTEM") {
if (!certSource.CertificateKey) {
errorCB({
372
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
373
description:
AuthProxy.prototype.MSG_MISSING_PARAMETER + "certificate key"
374
});
375
return false;
Kapsel Development
159
Kapsel Development
376
377
378
{
379
errorCB({
380
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
381
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "certificate key"
382
});
383
return false;
384
385
386
}
} else if (certSource.Source === "LOGON") {
if (!certSource.AppID) {
387
errorCB({
388
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
389
description:
AuthProxy.prototype.MSG_MISSING_PARAMETER + "AppID"
390
});
391
return false;
392
393
394
395
errorCB({
396
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
397
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "AppID"
398
});
399
return false;
400
401
} else {
402
160
errorCB({
Kapsel Development
403
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
404
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "certSource"
405
});
406
return false;
407
408
409
return true;
410
} catch (ex) {
411
errorCB({
412
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
413
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "certSource"
414
});
415
416
}
};
417
418
419
/**
420
* Send an HTTP(S) request to a remote server. This function
is the centerpiece of the AuthProxy plugin. It handles
421
* mutual authentication if a certificate source is
provided.
422
* The success callback is invoked upon any response from the
server, even responses not generally considered to be
423
* successful (such as 404 or 500 status codes) result in the
success callback being invoked. The error callback
424
* is reserved for problems that prevent the AuthProxy from
creating the request or contacting the server. It is, therefore,
425
* important to always check the status property on the object
given to the success callback.
426
name.
427
* @param {string} url The HTTP URL with format http(s)://
[user:password]@hostname[:port]/path.
Kapsel Development
161
Kapsel Development
428
* @param {Object} header HTTP header to send to the server.
This is an Object. Can be null.
429
* @param {string} requestBody Data to send to the server with
the request. Can be null.
430
* @param {sap.AuthProxy~successCallback} successCB Callback
method invoked upon a response from the server.
431
* @param {sap.AuthProxy~errorCallback} errorCB Callback
method invoked in case of failure.
432
433
* @param {string} [password] User password for basic
authentication.
434
* @param {number} [timeout] Timeout setting in seconds.
Default value (0) means there is no timeout.
435
* @param {Object} [certSource] Certificate description
object. It can be one of {@link sap.AuthProxy#CertificateFromFile},
436
* {@link sap.AuthProxy#CertificateFromStore}, or {@link
sap.AuthProxy#CertificateFromLogonManager}.
437
* @return {function} A JavaScript function object to abort
the operation. Calling the abort function results in neither the
success or error
438
* callback being invoked for the original request (excepting
the case where the success or error callback was invoked before
calling the
439
* abort function). Note: The request itself cannot be
unsent, and the server will still receive the request, but the
JavaScript will
440
441
* @example
442
* // callbacks
443
444
*
alert("Status: " +
JSON.stringify(serverResponse.status));
445
*
alert("Headers: " +
JSON.stringify(serverResponse.headers));
446
*
alert("Response: " +
JSON.stringify(serverResponse.response));
447
* }
448
449
*
alert("Error making request: " +
JSON.stringify(errorObject));
162
Kapsel Development
450
* }
451
452
* var abortFunction = sap.AuthProxy.sendRequest("POST",
"http://www.google.com", null, "THIS IS THE BODY", successCB,
errorCB);
453
454
* abortFunction();
455
456
* // To send a post request to the server with headers, call
the method
457
* sap.AuthProxy.sendRequest("POST", url, {HeaderName :
"Header value"}, "THIS IS THE BODY", successCB, errorCB);
458
459
* // To send a post request to the server with basic
authentication, call the method
460
* sap.AuthProxy.sendRequest("POST", url, headers, "THIS IS
THE BODY", successCB, errorCB, "username", "password");
461
462
* // To send a post request to the server with mutual
authentication, call the method
463
* sap.AuthProxy.sendRequest("POST", "https://hostname",
headers, "THIS IS THE BODY", successCB, errorCB, null,
464
*
null, 0, new
sap.AuthProxy.CertificateFromLogonManager("theAppId"));
465
*/
466
AuthProxy.prototype.sendRequest = function (method, url,
header, requestBody, successCB, errorCB, user, password, timeout,
certSource) {
467
468
// errorCB required.
469
errors
470
471
function");
472
// if error callback is invalid, throw an exception to
notify the caller
Kapsel Development
163
Kapsel Development
473
function");
474
475
476
// method required
477
if (!method) {
478
479
errorCB({
480
errorCode:
AuthProxy.prototype.ERR_MISSING_PARAMETER,
481
description:
AuthProxy.prototype.MSG_MISSING_PARAMETER + "method"
482
});
483
return;
484
485
486
487
488
if (method !== "GET" && method !== "POST" && method !==
"HEAD" && method !== "PUT" && method !== "DELETE") {
489
+ method);
490
errorCB({
491
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
492
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "method"
493
});
494
return;
495
496
497
498
// url required
499
if (!url) {
500
164
Kapsel Development
501
errorCB({
502
errorCode:
AuthProxy.prototype.ERR_MISSING_PARAMETER,
503
description:
AuthProxy.prototype.MSG_MISSING_PARAMETER + "url"
504
});
505
return;
506
507
508
509
// successCB required
510
if (!successCB) {
511
required");
512
errorCB({
513
errorCode:
AuthProxy.prototype.ERR_MISSING_PARAMETER,
514
description:
AuthProxy.prototype.MSG_MISSING_PARAMETER + "successCB"
515
});
516
return;
517
518
519
520
521
function");
522
errorCB({
523
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
524
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "successCB"
525
});
526
return;
527
Kapsel Development
165
Kapsel Development
528
529
530
531
errorCB({
532
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
533
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "user"
534
});
535
return;
536
537
538
539
540
errorCB({
541
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
542
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "password"
543
});
544
return;
545
546
547
548
549
errorCB({
550
errorCode:
AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
551
description:
AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "timeout"
552
});
553
return;
554
555
166
Kapsel Development
556
if (!this.validateCertSource(certSource, errorCB)) {
557
return;
558
559
560
561
try {
562
var client = new Client(method, url, header,
requestBody, successCB, errorCB, user, password, timeout,
certSource);
563
return client.send();
564
} catch (ex) {
565
errorCB({
566
errorCode: AuthProxy.prototype.ERR_UNKNOWN,
567
description: ex.message
568
});
569
570
571
};
572
573
/**
574
* Send an HTTP(S) GET request to a remote server.
convenience function that simply calls {@link
sap.AuthProxy#sendRequest}
This is a
575
* with "GET" as the method and null for the request body.
All given parameters are passed as-is to sap.AuthProxy.sendRequest.
576
* The success callback is invoked upon any response from the
server, even responses not generally considered to be
577
* successful (such as 404 or 500 status codes) result in the
success callback being invoked. The error callback
578
* is reserved for problems that prevent the AuthProxy from
creating the request or contacting the server. It is, therefore,
579
* important to always check the status property on the object
given to the success callback.
580
* @param {string} url The URL against which to make the
request.
581
* @param {Object} header HTTP header to send to the server.
This is an Object. Can be null.
Kapsel Development
167
Kapsel Development
582
* @param {sap.AuthProxy~successCallback} successCB Callback
method invoked upon a response from the server.
583
* @param {sap.AuthProxy~errorCallback} errorCB Callback
method invoked in case of failure.
584
585
* @param {string} [password] User password for basic
authentication.
586
* @param {number} [timeout] Timeout setting in seconds.
Default value (0) means there is no timeout.
587
* @param {Object} [certSource] Certificate description
object. It can be one of {@link sap.AuthProxy#CertificateFromFile},
588
* {@link sap.AuthProxy#CertificateFromStore}, or {@link
sap.AuthProxy#CertificateFromLogonManager}.
589
* @return {function} A JavaScript function object to abort
the operation. Calling the abort function results in neither the
success or error
590
* callback being invoked for the original request (excepting
the case where the success or error callback was invoked before
calling the
591
* abort function). Note: The request itself cannot be
unsent, and the server will still receive the request, but the
JavaScript will
592
593
* @example
594
595
*
alert("Status: " +
JSON.stringify(serverResponse.status));
596
*
alert("Headers: " +
JSON.stringify(serverResponse.headers));
597
if (serverResponse.responseText){
598
*
alert("Response: " +
JSON.stringify(serverResponse.responseText));
599
600
* }
601
602
*
alert("Error making request: " +
JSON.stringify(errorObject));
168
603
* }
604
Kapsel Development
605
* var abortFunction = sap.AuthProxy.get("http://
www.example.com", null, successCB, errorCB);
606
607
* abortFunction();
608
* // To send a GET request to the server with headers, call
the method
609
* sap.AuthProxy.get("http://www.example.com", {HeaderName :
"Header value"}, successCB, errorCB);
610
* // To send a GET request to the server with basic
authentication, call the method
611
* sap.AuthProxy.get("https://www.example.com", headers,
successCB, errorCB, "username", "password");
612
* // To send a GET request to the server with mutual
authentication, call the method
613
* sap.AuthProxy.get("https://www.example.com", headers,
successCB, errorCB, null, null, 0,
614
*
new
sap.AuthProxy.CertificateFromLogonManager("theAppId"));
615
*/
616
AuthProxy.prototype.get = function (url, header, successCB,
errorCB, user, password, timeout, certSource) {
617
return this.sendRequest("GET", url, header, null,
successCB, errorCB, user, password, timeout, certSource);
618
};
619
620
/**
621
* Delete a cached certificate from the keychain. iOS clients
always check the cached certificate first to see if it is available
before
622
* loading the certificate from the file system. If the cached
certificate is no longer valid, use this method to delete it from the
keychain.
623
624
* @param {sap.AuthProxy~deleteCertificateSuccessCallback}
successCB Callback method upon success.
625
* @param {sap.AuthProxy~errorCallback} [errorCB] Callback
method upon failure.
626
* @param {string} certificateKey The key of the certificate
to delete.
Kapsel Development
169
Kapsel Development
627
* @example
628
629
630
* }
631
632
*
alert("error deleting certificate: " +
JSON.stringify(error));
633
* }
634
* sap.AuthProxy.deleteCertificateFromStore(successCB,
errorCB, "certificateKeyToDelete");
635
*/
636
AuthProxy.prototype.deleteCertificateFromStore = function
(successCB, errorCB, certificateKey) {
637
cordova.exec(successCB, errorCB, "AuthProxy",
"deleteCertificateFromStore", [certificateKey]);
638
};
639
640
/**
641
* @private
642
*/
643
var Client = function (method, url, header, requestBody,
successCB, errorCB, user, password, timeout, certSource) {
644
645
//ios plugin parameter does not support object type,
convert Header and CertSource to JSON string
646
if (device.platform === "iOS" || (device.platform &&
device.platform.indexOf("iP") === 0)) {
647
if (header) {
648
header = JSON.stringify(header);
649
650
if (certSource) {
651
certSource = JSON.stringify(certSource);
652
653
}
}
654
170
Kapsel Development
655
this.Method = method;
656
this.Url = url;
657
this.Header = header;
658
this.RequestBody = requestBody;
659
this.SuccessCB = successCB;
660
this.ErrorCB = errorCB;
661
this.User = user;
662
this.Password = password;
663
this.Timeout = timeout;
664
this.CertSource = certSource;
665
this.IsAbort = false;
666
667
this.abort = function () {
668
669
this.IsAbort = true;
};
670
671
672
this.send = function () {
673
674
var args = [this.Method, this.Url, this.Header,
this.RequestBody, this.User, this.Password, this.Timeout,
this.CertSource];
675
676
var me = this;
677
678
679
680
return;
681
682
683
684
successCB(data);
};
685
Kapsel Development
171
Kapsel Development
686
687
688
return;
689
690
691
errorCB(data);
692
};
693
694
exec(successCallBack, errorCallBack, "AuthProxy",
"sendRequest", args);
695
696
return this.abort;
697
698
};
};
699
700
/**
701
* Generates an OData client that uses the AuthProxy plugin to
make requests. This is useful if you are using Datajs, but want
702
* to make use of the certificate features of AuthProxy.
Datajs is a JavaScript library useful for accessing OData services.
703
* Datajs has a concept of an HttpClient, which does the work
of making the request. This function generates an HttpClient that
704
* you can specify to Datajs so you can provide client
certificates for requests. If you want to use the generated HTTP
client
705
* for all future Datajs requests, you can do that by setting
the OData.defaultHttpClient property to the return value of this
706
* function. Once that is done, then doing OData stuff with
Datajs is almost exactly the same, but you can add a
707
* certificateSource to a request.
708
* @example
709
* OData.defaultHttpClient =
sap.AuthProxy.generateODataHttpClient();
172
710
711
Kapsel Development
712
* fileCert = new sap.AuthProxy.CertificateFromFile("mnt/
sdcard/cert.p12", "password", "certKey");
713
714
* // This is the same request object you would have created
if you were just using Datajs, but now
715
716
* var createRequest = {
717
*
example.svc",
requestUri: "http://www.example.com/stuff/etc/
718
certificateSource : fileCert,
719
user : "username",
720
password : "password",
721
method : "POST",
722
data:
723
724
725
726
727
* }
728
729
730
* OData.request( createRequest, successCallback,
failureCallback );
731
732
*/
733
734
AuthProxy.prototype.generateODataHttpClient = function () {
var httpClient = {
735
736
var url, requestHeaders, requestBody, statusCode,
statusText, responseHeaders;
737
var responseBody, requestTimeout, requestUserName,
requestPassword, requestCertificate;
738
739
Kapsel Development
173
Kapsel Development
740
url = request.requestUri;
741
requestHeaders = request.headers;
742
requestBody = request.body;
743
744
745
var response = {
746
requestUri: url,
747
statusCode: data.status,
748
statusText: data.status,
749
headers: data.headers,
750
body: (data.responseText ?
data.responseText : data.responseBase64)
751
};
752
753
if (response.statusCode >= 200 &&
response.statusCode <= 299) {
754
if (success) {
755
success(response);
756
757
} else {
758
if (error) {
759
error({
760
761
request: request,
762
response: response
763
});
764
765
766
}
};
767
768
769
770
174
Kapsel Development
771
message: data
772
});
773
774
}
};
775
776
if (request.timeoutMS) {
777
778
779
780
if (request.certificateSource) {
781
requestCertificate =
request.certificateSource;
782
783
784
if (request.user) {
785
786
requestUserName = request.user;
}
787
788
if (request.password) {
789
790
requestPassword = request.password;
}
791
792
client =
AuthProxy.prototype.sendRequest(request.method || "GET", url,
requestHeaders, requestBody, successCB, errorCB, requestUserName,
requestPassword, requestTimeout, requestCertificate);
793
794
result = {};
795
result.abort = function () {
796
client.abort();
797
798
799
800
Kapsel Development
if (error) {
error({
message: "Request aborted"
175
Kapsel Development
801
});
802
803
};
804
return result;
805
806
};
807
return httpClient;
808
};
809
810
811
812
module.exports = AuthProxyPlugin;
813
814
815
/**
816
817
818
* @callback sap.AuthProxy~errorCallback
819
820
* @param {Object} errorObject An object containing two
properties: 'errorCode' and 'description.'
821
* The 'errorCode' property corresponds to one of the {@link
sap.AuthProxy} constants. The 'description'
822
* property is a string with more detailed information of what
went wrong.
823
824
* @example
825
* function errorCallback(errCode) {
826
*
//Set the default error message. Used if an invalid code
is passed to the
176
827
828
829
830
switch (errCode) {
Kapsel Development
831
case sap.AuthProxy.ERR_INVALID_PARAMETER_VALUE:
832
833
break;
834
835
836
break;
837
case sap.AuthProxy.ERR_HTTP_TIMEOUT:
838
839
break;
840
};
841
842
console.error(msg);
843
case sap.AuthProxy.ERR_MISSING_PARAMETER:
844
*
navigator.notification.alert(msg, null, "AuthProxy
Error", "OK");
845
* };
846
*/
847
848
/**
849
* Callback function that is invoked upon a response from the
server.
850
851
* @callback sap.AuthProxy~successCallback
852
853
* @param {Object} serverResponse An object containing the
response from the server. Contains a 'headers' property,
854
855
* 'headers' is an object containing all the headers in the
response.<br/>
856
* 'status' is an integer corresponding to the HTTP status
code of the response. It is important to check the status of
857
* the response, since <b>this success callback is invoked
upon any response from the server</b> - including responses that
are
Kapsel Development
177
Kapsel Development
858
* not normally thought of as successes (for example, the
status code could be 404 or 500).<br/>
859
* 'responseText' is a string containing the body of the
response.
860
*/
861
862
/**
863
* Callback function that is invoked upon successfully
deleting a certificate from the store.
864
865
* @callback sap.AuthProxy~deleteCertificateSuccessCallback
866
*/
With the Logger plugin, you can enable an application to write log entries that can then be
automatically uploaded to SAP Mobile Platform Server for analysis by using the
sap.Logger.upload() method. If you add the Settings plugin to your project files,
sap.Logger.upload() is called with a logon success event (for example, when the
application is launched or resumed and logon is successful) so the log file is uploaded
automatically. If you do not use the Settings plugin, you can upload log files only by calling the
sap.Logger.upload() method manually.
You can build in support for logging so that an administrator can remotely set the appropriate
log level from SAP Mobile Platform Server. The Kapsel Logger plugin can define each log
message with specific levels, such as Debug and Error, which enables you to filter the log
message by priority level. The Kapsel Logger plugin mirrors the OData logger library so that it
can collect all of the logging data produced by the OData library. The Kapsel plugins use
OData libraries in several places so that it can help see and trace the plugins' logging data.
Using the provided sap.Logger.upload() method allows you to log events that occur
on the device and send them to SAP Mobile Platform Server, where an Administrator can view
178
Kapsel Development
them and remotely set the appropriate log level to control the amount of information that is
written to the log.
This shows the index.html file for a sample app, which has the appID of
"com.mycompany.logger" with the server connection information. This information allows
the app to register with the appID on SAP Mobile Platform Server. This sample app logs
messages with the log level and uploads a log file to SAP Mobile Platform Server. For
example, to log messages with DEBUG log level, you can call the
sap.Logger.debug(...) method. You can also use other methods for logging with
other log levels (INFO, WARN and ERROR).
<html>
<head>
<script type="text/javascript" charset="utf-8"
src="cordova.js"></script>
<script>
logonView = null;
logon = null;
applicationContext = null;
function init() {
var appId = "com.mycompany.logger"; // Change this to
app id on server
// Optional initial connection context var context = {
"serverHost": "server.sap.corp", //Place your SAP
Mobile Platform server 3.0 name here
"https": "false",
"serverPort": "8080",
"user": "user", //Place your user name for the OData
Endpoint here
"password": "xxxxxxx", //Place your password for the
OData Endpoint here
"communicatorId": "REST",
"passcode": "password",
"unlockPasscode": "password"
};
sap.Logon.init(function() { }, function() {alert("Logon
Failed"); }, appId, context, sap.logon.IabUi);
sap.Logger.setLogLevel(sap.Logger.DEBUG);
}
function logMessage() {
var employee = {name: "Dan", location : "Waterloo"};
console.log("The value of employee is " +
JSON.stringify(employee));
}
function logMessage2() {
sap.Logger.debug("Debug log message");
sap.Logger.info("Info log message");
sap.Logger.warn("Warn log message");
sap.Logger.error("Error log message");
}
function uploadLog() {
sap.Logger.upload(function() {
alert("Upload Successful");
}, function(e) {
Kapsel Development
179
Kapsel Development
alert("Upload Failed. Status: " + e.statusCode + ",
Message: " + e.statusMessage);
});
}
document.addEventListener("deviceready", init, false);
</script>
</head>
<body>
<h1>Logger Sample</h1>
<button id="log" onclick="logMessage()">Log Message with
console</button><br>
<button id="log" onclick="logMessage2()">Log Message with
Logging Plugin</button><br>
<button id="upload" onclick="uploadLog()">Upload Log</
button>
</body>
</html>
ERROR
WARN
INFO
DEBUG
By default, only error level logs are captured. Use the setLogLevel to capture other levels.
If the log level is DEBUG, all log level messages are stored. If it is WARN, the uploaded log
contains WARN and ERROR messages.
On iOS, if the log level is ERROR, then only ERROR level messages are displayed in the
console, even if other log level messages are generated. But if the current log level is DEBUG,
INFO, or WARN, all generated log messages, regardless of log level, are displayed in the
console.On Android, all generated log messages, regardless of log level, are shown in the
Android log cat view (console).
To upload the log to the server, in the logMessageInfoToSMP() function, enter:
sap.Logger.setLogLevel(sap.Logger.INFO,
function(logLevel) {console.log("Log level set");},
function() {console.log("Failed to set log level");});
Limitations
On Android, the maximum for log entries is 10,000. The oldest 200 log entries are removed if
the 10,000 maximum is reached. This applies to both the device and emulator.
180
Kapsel Development
On iOS simulators, the Logger plugin may behave in unpredictable ways, as it is intended for
use with a device. On iOS devices, there is no explicit maximum for log entries, however, old
messages are removed from the device after a time.
Adding the Logger Plugin
Install the Logger plugin using the Cordova command line interface.
Prerequisites
Task
1. Add the Logger plugin by entering the following at the command prompt, or terminal:
On Windows:
cordova -d plugin add <SDK_HOME>\MobileSDK3\KapselSDK
\plugins\logger
On Mac:
cordova -d plugin add ~<SDK_HOME>/MobileSDK3/KapselSDK/
plugins/logger
Note: The path you enter to the Kapsel plugin must be the absolute path (not relative
path).
2. (Optional) To see a list of installed plugins in your Cordova project, open a command
prompt or terminal window, navigate to your Cordova project folder, and enter:
cordova plugins
The Cordova command line interface returns a JSON array showing installed plugins, for
example:
[ 'org.apache.cordova.core.camera',
'org.apache.cordova.core.device-motion',
'org.apache.cordova.core.file' ]
In this example, the Cordova project has the Cordova core Camera, Accelerator (devicemotion), and File plugins installed.
3. Modify the files in the www folder for the project as necessary, then copy them to the
platform directories by running:
cordova -d prepare android
cordova -d prepare ios
4. Use the Android IDE or Xcode to deploy and run the project.
Kapsel Development
181
Kapsel Development
Note: If you are using an iOS device, remember to add the "clienthubEntitlements" to the
Keychain Groups in the Entitlement section in Xcode.
Viewing Client Logs
(Applies only to hybrid) Download and view a client log associated with the selected
application registration. The developer must have implemented Logger code in the
application code, and the application must be registered and collecting data. The log content
varies by device type and operating system.
1. From Management Cockpit, select Registrations on the Home screen to view application
connections. Alternatively, in the Applications tab, click the Registrations tab.
Information for up to 200 registered applications appears.
2. Use the search, sorting, and filtering options to locate the registration in which you are
interested:
3. Click the red client log icon to display the Client Logging dialog. In some cases, a list of
client logs appears.
a) Click Enable Log Upload.
b) Select the log level in Log Type.
c) Click Save to save the modified setting of whether to allow uploading of client logs,
and the level at which the client should log.
The red client log icon turns green.
d) Click a log file name to download the log and open it in your selected viewer.
Client Logs
(Applies only to hybrid) If client logging has been enabled for a hybrid application and log
data is available, you can view the client long for the selected application registration.
Note: The log format varies by device type and operating system. Following are example log
excerpts for Android and iOS.
Hybrid Client Log - Android Example
1377125811306
Debug Tag
Debug Message
null (com.sap.mp.cordova.plugins.logger.Logger:execute:54)
1893
1377125813056
Info Tag
Info Message
null (com.sap.mp.cordova.plugins.logger.Logger:execute:61)
1893
1377125814165
Warn Tag
Warn Message
null (com.sap.mp.cordova.plugins.logger.Logger:execute:68)
182
Kapsel Development
1893
1377125815157
Error Tag
Error Message
null (com.sap.mp.cordova.plugins.logger.Logger:execute:75)
1893
Kapsel Development
183
Kapsel Development
CFLog Thread = c07
ASLMessageID = 142694
Time = Aug 20, 2013, 4:37:22 PM
TimeNanoSec = 152145000
Level = 4
PID = 4823
UID = 966313393
GID = 1824234391
ReadUID = 966313393
Host = PALM00545086A
Sender = KAPSEL326
Facility = com.sap.kapsel326
Message = Multi-tasking -> Device: YES, App: YES
CFLog Local Time = 2013-08-20 16:37:22.151
CFLog Thread = c07
Testing Logging
The log file is located in SMP_HOME\Server\log\clientlogs
\<application_id>\<application_registration_id>\Log.txt.
1. Run your project with the Android IDE or Xcode.
2. In Management Cockpit, enable the upload log function.
Note: For the call to sap.Logger.upload() to succeed, the Log Upload checkbox
on the registration ID in the Management Cockpit must be checked.
3. View the uploaded logs in the Management Cockpit.
Kapsel Logger API Reference
The Kapsel Logger API Reference provides usage information for Logger API classes and
methods, as well as provides sample source code.
Logger namespace
The Kapsel Logger plugin provides a Cordova plugin wrapper around the SAP Mobile
Platform client logging API.
It has ERROR, WARN, INFO and DEBUG log level and log messages are captured based on
the selected log level. Android and iOS logger default log level is ERROR. By default only
ERROR level logs are captured. The sap.Logger.setLogLevel() method is used to set other
levels. To get log messages for all log levels, you must set the log level to DEBUG. (DEBUG <
INFO < WARN < ERROR)
Using the provided sap.Logger.upload() method allows you to upload a log file to SAP Mobile
Platform Server, where an Administrator can view them and remotely set the appropriate log
184
Kapsel Development
level to control the amount of information that is written to the log. When the
sap.Logger.upload() method is triggered, a log file (not multiple) is uploaded. If the Log
Upload check box is enabled in the Management Cockpit, the client can upload a log file by
calling sap.Logger.upload(). If the Log Upload check box is disabled on the server, the client
cannot upload without getting an HTTP/1.1 403 Forbidden error. For the Logger plugin, you
must call sap.Logger.upload() to upload the log file and implement an upload button or
something that does the upload from within the app to the server.
To upload a log file with the Logger plugin, these conditions must be met: 1) Log Upload
check box is enabled on the server 2)The sap.Logger.upload() method is called by developer.
This is the expected work flow with the current architecture.
1) If a user has an issue, such as an exception error, he/she reports it by email or a call to
customer center.
2) The Administrator (or developer) enables the app on the server. Then the Administrator lets
the user know that he/she can upload the log file.
3) The user uploads a log file to the server. The Administrator gets the uploaded log file in the
Management Cockpit.
4) The Administrator sends the file to the app's developer to debug.
On iOS, if the current log level is ERROR(default level), only ERROR level messages are
displayed in the console even if other log level messages are generated. But if the current log
level is DEBUG, INFO or WARN, all generated log messages, regardless of log level are
displayed in the console.
On Android, all generated log messages, regardless of log level, are displayed in the Android
log cat view (console).
When you use the Settings plugin, it 1) Gets the log level from the server, 2) Sets it into Logger
on the client and 3) Calls sap.Logger.upload() upon a logon success event (when the app is
launched or resumed and logon is successful). This means that the setting plugin retrieves the
Kapsel Development
185
Kapsel Development
selected log level(type) from the Management Cockpit on the server, sets the log level to the
Logger plugin and then automatically uploads a log file to the server. If the Settings plugin is
not added in the project, a log file can be uploaded only by calling the sap.Logger.upload()
method manually by developer. In the Management Cockpit (client logging dialog box), the
Log Upload check box can enable and disable and you can choose the log type(level). The
uploaded log files are listed in the Management Cockpit. There are seven log types (NONE,
FATAL, ERROR, WARNING, INFO, DEBUG and PATH) on the server. Since the Kapsel
Logger plugin supports only DEBUG, INFO, WARN and ERROR, the Logger plugin
implicitly matches FATAL to ERROR and PATH to DEBUG. If NONE is set on server admin
UI, logger sets it to default log level.
To add the Logger plugin to your project, use the following command:
Cordova plugin add <path to directory containing Kapsel plugins>\logger
To remove the Logger plugin from your project, use the following command:
cordova plugin rm com.sap.mp.cordova.plugins.logger
Members
186
Name
Description
Kapsel Development
Methods
Name
Description
debug( message, [tag], [successCallback], [error- Add a debug message to the log. This function
Callback] ) on page 188
logs messages with 'DEBUG' log level.
error( message, [tag], [successCallback], [errorCallback] ) on page 189
Set log level. This function sets the log level for
logging.
upload( successCallback, errorCallback ) on page Upload a log file that contains log entries to SAP
Mobile Platform server.
195
Source
Source
Kapsel Development
187
Kapsel Development
Syntax
<static, constant> Logger#ERROR : String
Example
sap.Logger.setLogLevel(sap.Logger.ERROR);
Source
Source
Source
188
Kapsel Development
Parameters
Name
Type
Argument
Description
message
String
tag
String
(optional)
successCallback
function
(optional)
errorCallback
function
(optional)
Example
sap.Logger.debug("debug message", "DEBUG_TAG");
Source
Kapsel Development
189
Kapsel Development
Syntax
<static> error( message, [tag], [successCallback], [errorCallback] )
Parameters
Name
Type
Argument
Description
message
String
tag
String
(optional)
successCallback
function
(optional)
errorCallback
function
(optional)
Example
sap.Logger.error("error message", "ERROR_TAG");
Source
190
Kapsel Development
Type
successCallback
function
errorCallback
function
Argument
Description
success callback method upon success
state.When current log
level is successfully retrieved, it is fired with
the current log level.
[DEBUG, INFO,
WARN, ERROR] Log
level of String type will
be passed to success
callback. Default log
level is ERROR.
(optional)
Example
sap.Logger.getLogLevel(function(logLevel) {
alert("Log level is " + logLevel);
}, function() {
Kapsel Development
191
Kapsel Development
alert("Failed to get log level");
});
Source
192
Name
Type
Argument
Description
message
String
tag
String
(optional)
successCallback
function
(optional)
Kapsel Development
errorCallback
function
(optional)
Example
sap.Logger.info("info message", "INFO_TAG");
Source
Coverage of logging data in each log level: DEBUG < INFO < WARN < ERROR.
Following is the expected behavior to cover log messages at specific log levels.
Error : only error
Warn : error, warn
Info : error, warn, info
Debug : error, warn, info, debug
Kapsel Development
193
Kapsel Development
For example, if you want to get all log messages, you need to set it to 'Debug' level. If WARN
level is set, logging data contains WARN and ERROR messages.
Default log level is ERROR.
Syntax
<static> setLogLevel( level, [successCallback], [errorCallback] )
Parameters
Name
Type
Argument
Description
level
String
successCallback
function
(optional)
errorCallback
function
(optional)
Example
sap.Logger.setLogLevel(sap.Logger.DEBUG, function(logLevel) {
alert("Log level set");
}, function() {
alert("Failed to set log level");
});
194
Kapsel Development
Source
This function uploads a log file, which is helpful for developers who want to collect logging
data from the app to trace bugs and issues. It uploads a log file which contains log entries based
on log level. Developers can access the log data in the Management Cockpit or a specific
folder in the installed server directly.
On iOS logger, when uploading a log file, the uploaded log messages are filtered by the log
level at upload. For example, when you upload a log file at the ERROR log level, uploaded log
messages contain only error log level messages. When you upload a log file at the INFO level,
uploaded log messages contain error, warn, and info log level messages.
Android logger just filters the generated log messages "at the log level." In other words, the
already generated and filtered log messages at another log level are not affected by current log
level. Log messages are not filtered at uploading. For example, set the log level to DEBUG and
log four levels (DEBUG, INFO, WARN and ERROR) of messages. At this time, the Android
logger has four log level messages. If you set the log level to WARN and upload a log file, the
log file has four log level messages which were already generated at the DEBUG level.
Syntax
<static> upload( successCallback, errorCallback )
Parameters
Name
Type
Description
successCallback
function
Kapsel Development
195
Kapsel Development
errorCallback
function
error callback method upon error state.If there is a connectivity error, such as an HTTP error
or unknown server error, this
method is fired with http statusCode and statusMessage for error.
Example
sap.Logger.upload(function() {
alert("Upload Successful");
}, function(e) {
alert("Upload Failed. Status: " + e.statusCode + ", Message: " +
e.statusMessage);
});
Source
196
Name
Type
message
String
tag
String
Argument
Description
log message to be logged
(optional)
Kapsel Development
successCallback
function
(optional)
errorCallback
function
(optional)
Example
sap.Logger.warn("warn message", "WARN_TAG");
Source
// ${project.version}
3
4
/**
5
* The Kapsel Logger plugin provides a Cordova plugin wrapper
around the SAP Mobile Platform client logging API.
6
* <br><br>
Kapsel Development
197
Kapsel Development
8
* The Logger plugin has ERROR, WARN, INFO, and DEBUG log
levels and log messages are captured based on the configured and
selected log level.
9
* A Kapsel application can be set to these log levels by
programmatic control, and by the administrator changing a setting on
the server.
10
* For Android and iOS, the default log level is ERROR, so by
default only ERROR level logs are captured.
11
* sap.Logger.setLogLevel() method is used to set other
levels. If you want to get log messages at all log levels,
12
* you must set the log level to DEBUG. (DEBUG < INFO < WARN <
ERROR) <br>
13
* If the log level is set to DEBUG, the application captures
all log messages. <br>
14
* If you set the log level to INFO, the application captures
INFO, WARN, and ERROR log messages. <br>
15
* If you set the log level to WARN, the application captures
WARN and ERROR log messages. <br>
16
* If you set the log level to ERROR, the application captures
only Error log messages.
17
* <br><br>
18
19
* Using the provided sap.Logger.upload() method allows
developers to upload a log file to SAP Mobile Platform Server,
20
* where an administrator can view them and remotely set the
appropriate log level to control the amount of information
21
* that is written to the log. When the sap.Logger.upload()
method is triggered, a log file will be uploaded.
22
* If the Log Upload checkbox is selected in the Management
Cockpit, the client can upload a log file by calling
sap.Logger.upload().
23
* If the Log Upload checkbox is disabled in the Management
Cockpit, the client does not upload the log file to the server. The
attempt to upload causes an "HTTP/1.1 403 Forbidden" error.
24
* To support manual uploading of the log, you should
implement a button or some other mechanism that calls
sap.Logger.upload() when needed.
25
* <br>
26
* For the Logger plugin to upload a log file these conditions
must be met: 1) Log Upload checkbox enabled In the Management Cockpit
2) sap.Logger.upload() is called by developer.
198
Kapsel Development
27
* <br>
28
* The expected work flow, with the current architecture
consists of the following: <br>
29
* 1) If a user has an issue that needs to be analyzed by an
administrator or developer, the user reports the issue as
appropriate.<br>
30
* 2) The administrator, or developer, enables the log
collection for the user on the SAP Mobile Platform server.<br>
31
* 3) The administrator lets the user know that he, or she,
can upload log file. <br>
32
* 4) The user uploads thelog file to the server, and the
administrator gets the uploaded log file in the Management
Cockpit.<br>
33
debug.
34
* <br><br>
35
36
* Currently, on iOS, if the current log level is ERROR
(default level), only ERROR level messages are displayed on the
console
37
* even if other log level messages are generated. But if the
current log level is DEBUG, INFO, or WARN,
38
* all generated log messages, regardless of log level, are
displayed on the console. <br>
39
* On Android, all generated log messages, regardless of log
level, are displayed in the Android logcat view (console)
40
* <br><br>
41
42
* When the Kapsel Settings plugin is added to the project,
Settings will: 1) Get log level from the server 2) Set it into Logger
on the client
43
* 3) Call sap.Logger.upload() after a logon success event,
for example, when the app is launched or resumed and logon is
successful.
44
* The Settings plugin retrieves the selected log level(type)
from the Management Cockpit on the server,
45
* sets the log level to Logger plugin, and then automatically
uploads a log file to the server.
46
* If the Settings plugin is not added to the project, a log
file can be uploaded only by the developer calling the
sap.Logger.upload() method manually.
Kapsel Development
199
Kapsel Development
47
* In the Management Cockpit, in the Client Logging dialog
box, the Log Upload checkbox is able to enable or disable log file
upload, and you can choose the log type(level).
48
* You can also view a list of the uploaded log files. On the
server side, there are seven log types: NONE, FATAL, ERROR, WARNING,
INFO, DEBUG and PATH.
49
* Since the Kapsel Logger plugin supports only DEBUG, INFO,
WARN, and ERROR, the Logger plugin implicitly matches FATAL to ERROR,
and PATH to DEBUG.
50
* If NONE is set in the Management Cockpit,
Logger sets it to default log level.
51
* <br><br>
52
53
54
* <a href="http://cordova.apache.org/docs/en/edge/
guide_cli_index.md.html#The%20Command-line%20Interface">Cordova
CLI</a>.<br>
55
* <br>
56
* To add the Logger plugin to your project, use the following
command:<br>
57
* Cordova plugin add <path to directory containing Kapsel
plugins>\logger<br>
58
* <br>
59
* To remove the Logger plugin from your project, use the
following command:<br>
60
61
* <br>
62
63
* @namespace
64
* @alias Logger
65
* @memberof sap
66
*/
67
68
69
200
Logger = function () {
/**
70
71
* @private
Kapsel Development
72
73
*/
var format = function (message) {
74
{
75
return "";
76
77
78
79
return message.toString();
}
80
81
82
/**
83
84
level.
85
86
* @memberof sap.Logger
87
* @method debug
88
89
* @param {String} [tag]
Tag value added to the log entry
used to indicate the source of the message (ex. SMP_LOGGER,
SMP_AUTHPROXY).
90
* @param {function} [successCallback] Callback function
called when the message has been successfully added to the log.
91
*
to success callback.
92
* @param {function} [errorCallback]
Callback function
called when an error occurs while adding the message to the log.
93
* Since Kapsel Logger native code will always call the
success callback function, the
94
* errorCallback function will be executed by Cordova if an
error or exception occurs
95
96
* @public
97
* @memberof sap.Logger
98
* @example
Kapsel Development
201
Kapsel Development
99
100
*/
101
this.debug = function (message, tag, successCallback,
errorCallback) {
102
exec(successCallback, errorCallback, "Logging",
"logDebug", [format(message), tag]);
103
104
105
/**
106
107
level.
108
109
* @memberof sap.Logger
110
* @method info
111
112
* @param {String} [tag]
Tag value added to the log entry
used to indicate the source of the message (for example, SMP_LOGGER,
SMP_AUTHPROXY).
113
* @param {function} [successCallback] Callback function
called when the message has been successfully added to the log.
114
115
* @param {function} [errorCallback]
Callback function
called when an error occurs while adding the message to the log.
116
* Since Kapsel Logger native code will always call the
success callback function, the
117
* errorCallback function will be executed by Cordova if
an error or exception occurs
118
119
* @public
120
* @memberof sap.Logger
121
* @example
122
123
*/
124
this.info = function (message, tag, successCallback,
errorCallback) {
202
Kapsel Development
125
exec(successCallback, errorCallback, "Logging",
"logInfo", [format(message), tag]);
126
127
128
/**
129
130
level.
131
132
* @memberof sap.Logger
133
* @method warn
134
135
* @param {String} [tag]
Tag value added to the log entry
used to indicate the source of the message (for example, SMP_LOGGER,
SMP_AUTHPROXY).
136
* @param {function} [successCallback] Callback function
called when the message has been successfully added to the log.
137
138
* @param {function} [errorCallback]
Callback function
called when an error occurs while adding the message to the log.
139
* Since Kapsel Logger native code will always call the
success callback function, the
140
* errorCallback function will be executed by Cordova if
an error/exception occurs
141
142
* @public
143
* @memberof sap.Logger
144
* @example
145
146
*/
147
this.warn = function (message, tag, successCallback,
errorCallback) {
148
exec(successCallback, errorCallback, "Logging",
"logWarning", [format(message), tag]);
149
150
Kapsel Development
203
Kapsel Development
151
/**
152
153
level.
154
155
* @memberof sap.Logger
156
* @method error
157
158
* @param {String} [tag]
Tag value added to the log entry
used to indicate the source of the message (for example, SMP_LOGGER,
SMP_AUTHPROXY).
159
* @param {function} [successCallback] Callback function
called when the message has been successfully added to the log.
160
161
* @param {function} [errorCallback]
Callback function
called when an error occurs while adding the message to the log.
162
* Since Kapsel Logger native code will always call the
success callback function, the
163
* errorCallback function will be executed by Cordova if
an error or exception occurs
164
165
* @public
166
* @memberof sap.Logger
167
* @example
168
169
*/
170
this.error =
errorCallback) {
171
exec(successCallback, errorCallback, "Logging",
"logError", [format(message), tag]);
172
173
174
204
/**
175
176
Kapsel Development
177
* Coverage of logging data in each log level:
INFO < WARN < ERROR. <br>
DEBUG <
178
* Following is the expected behavior to cover log
messages at specific log levels: <br>
179
180
WARN
181
INFO
182
183
* For example, if you want to get all log messages, you
need to set the log to the 'Debug' level.
184
* If the WARN level is set, logging data contains WARN and
ERROR messages. <br>
185
186
187
* @memberof sap.Logger
188
* @method setLogLevel
189
* @param {String} level Log level to set [DEBUG, INFO,
WARN, ERROR]
190
* @param {function} [successCallback] Callback function
called when the log level has been successfully set.
191
192
* @param {function} [errorCallback]
Callback function
called when an error occurs while setting the log level.
193
* Since Kapsel Logger native code will always call the
success callback function, the
194
* errorCallback function will be executed by Cordova if
an error or exception occurs
195
196
* @memberof sap.Logger
197
* @example
198
* sap.Logger.setLogLevel(sap.Logger.DEBUG,
successCallback, errorCallback);
199
200
* function successCallback() {
201
202
* }
Kapsel Development
205
Kapsel Development
203
204
* function errorCallback() {
205
206
* }
207
*/
208
this.setLogLevel = function (level, successCallback,
errorCallback) {
209
210
level = "ERROR";
211
212
level = "DEBUG";
213
214
level = "WARN";
215
216
217
level = "DEBUG";
218
219
level = "INFO";
220
221
level = "ERROR";
222
223
exec(successCallback, errorCallback, "Logging",
"setLogLevel", [level]);
224
225
226
/**
227
228
229
* Use this function to know what kind of log level
messages can be generated and affected at the current log level.
206
230
231
* @memberof sap.Logger
232
* @method getLogLevel
Kapsel Development
233
* @param {function} successCallback Callback function
called when the log level has been successfully retrieved.
234
* When the current log level is successfully retrieved,
it is fired with the current log level. [DEBUG, INFO, WARN, ERROR]
235
callback.
236
237
* @param {function} [errorCallback] Callback function
called when an error occurs while getting the current log level. For
this method, error callback is optional.
238
* Since Kapsel Logger native code will always call the
success callback function, the
239
* errorCallback function will be executed by Cordova if
an error or exception occurs
240
241
* @memberof sap.Logger
242
* @example
243
* sap.Logger.getLogLevel(successCallback,
errorCallback);
244
245
* function successCallback(logLevel) {
246
247
* }
248
249
* function errorCallback() {
250
251
* }
252
*/
253
this.getLogLevel = function(successCallback,
errorCallback) {
254
exec(successCallback, errorCallback, "Logging",
"getLogLevel",[]);
255
256
257
/**
258
* Upload a log file, with log entries, to SAP Mobile
Platform server.<br>
Kapsel Development
207
Kapsel Development
259
* This function uploads a log file, which is helpful for
collecting logging data from the app to trace bugs and issues.
260
* It uploads a log file, which contains log entries based
on log level.
261
* Developers can access the log data in the Management
Cockpit and/or a specific folder in installed server
directly.<br><br>
262
263
* On iOS, the uploaded log messages are filtered by the
log level at upon upload.
264
* For example, when you upload a log file with an ERROR
log level, the uploaded log messages contain only ERROR log level
messages.
265
* When you upload log files with an INFO level, uploaded
log messages contain ERROR, WARN, and INFO log level messages.
266
267
* <br><br>
268
* On Android, generated log messages are filtered "at the
log level."
269
* In other words, the already generated and filtered log
messages at another log level are not affected by the current log
level.
270
* Log messages are not filtered upon upload. For example,
if you set the log level to DEBUG log messages are filtered at four
levels (DEBUG, INFO, WARN, and ERROR.
271
* Logger on Android has four log levels messages. So, if
you set the log level to WARN and upload a log file, the log file has
four log level messages that were already generated at the DEBUG
level.
272
273
* @memberof sap.Logger
274
* @method upload
275
* @param {function} successCallback Callback function
called when a log file is successfully uploaded to the server.
276
* When a log file is successfully uploaded, it is fired.
(with http statusCode and statusMessage for success)
277
* @param {function} errorCallback
Callback function
called when an error occurs while uploading a log file to the
server.
278
* If there is a connectivity error, such as an HTTP error,
or unknown server error,
208
Kapsel Development
279
error.
280
* @public
281
* @memberof sap.Logger
282
* @example
283
* sap.Logger.upload(successCallback, errorCallback);
284
285
* function successCallback() {
286
287
* }
288
289
* function errorCallback(e) {
alert("Upload Successful");
290
*
alert("Upload Failed. Status: " + e.statusCode + ",
Message: " + e.statusMessage);
291
* }
292
*/
293
{
this.upload =
294
sap.Logon.unlock(function (connectionInfo) {
295
296
connectionInfo.applicationId =
sap.Logon.applicationId;
297
298
exec(successCallback, errorCallback, "Logging",
"uploadLog", [connectionInfo]);
299
}, function () {
300
"Logon failed"});
301
});
302
303
errorCallback({statusCode : 0, statusMessage :
}
}
304
305
/**
306
* Constant variable for Error log level. It contains "ERROR"
string.
Kapsel Development
209
Kapsel Development
307
* @memberof sap.Logger
308
* @constant
309
* @type String
310
* @example
311
* sap.Logger.setLogLevel(sap.Logger.ERROR);
312
*/
313
Logger.prototype.ERROR = "ERROR";
314
315
/**
316
* Constant variable for Warning log level. It contains "WARN"
string.
317
* @memberof sap.Logger
318
* @constant
319
* @type String
320
* @example
321
* sap.Logger.setLogLevel(sap.Logger.WARN);
322
*/
323
Logger.prototype.WARN = "WARN";
324
325
/**
326
* Constant variable for Information log level. It contains
"INFO" string.
327
* @memberof sap.Logger
328
* @constant
329
* @type String
330
* @example
331
* sap.Logger.setLogLevel(sap.Logger.INFO);
332
*/
333
Logger.prototype.INFO = "INFO";
334
335
/**
336
* Constant variable for Debug log level. It contains "DEBUG"
string.
210
Kapsel Development
337
* @memberof sap.Logger
338
* @constant
339
* @type String
340
* @example
341
* sap.Logger.setLogLevel(sap.Logger.DEBUG);
342
*/
343
Logger.prototype.DEBUG = "DEBUG";
344
345
346
Note: As a best practice, you should rarely use the unregister function. It is explained in detail
at http://developer.android.com/google/gcm/adv.html#unreg.
Kapsel Development
211
Kapsel Development
212
Kapsel Development
Creating an App ID
Create a new App ID for the application.
As a convention, the App ID is in the form of a reversed addresse, for example,
com.example.MyPushApp. The App ID must not contain a wildcard character ("*").
1. Go to the Apple Developer Member Center Web site, log in, if required, and select
Certificates, Identifiers & Profiles.
2. Select Identifiers > App IDs, and click the +.
3. Enter a name for your App ID, and, under App Service, select Push Notifications.
4. Accept the default App ID prefix, or choose another one.
5. Under App ID Suffix, select Explicit App ID, and enter your iOS app's Bundle ID.
This string should match the Bundle Identifier in your iOS app's Info.plist.
6. Select Continue.
Verify that all the values are correct. Push Notifications should be enabled, and the
Identifier field should match your app's Bundle Identifier (plus App ID Prefix).
7. Click Submit.
Configuring the App ID for Push Notifications
Once you create an App ID, you must configure it for push notifications.
1. From the list of iOS App IDs, select the App ID to configure, then select Settings.
2. Scroll down to the Push Notifications section and, under Development SSL Certificate,
select Create Certificate.
Here you can create both a Development SSL Certificate and a Production SSL Certificate.
3. Follow the instructions for creating a Certificate Signing Request (CSR), select Continue,
then select Choose File to locate the .certSigningRequest you created.
4. Click Generate.
5. Click Done once the certificate is ready, and download the generated SSL certificate from
the iOS App ID Settings screen.
6. Install the SSL in your Keychain.
a) In Keychain Access, under My Certificates, find the certificate you just added, rightclick on it, select Export Apple Development IOS Push Services, and save it as
a .p12 file.
Note: Do not enter an export password when prompted. You may, however, need to
enter your OSX password to allow Keychain Access to export the certificate from your
keychain.
Kapsel Development
213
Kapsel Development
214
Kapsel Development
console. Usually, it looks similar to this: https://code.google.com/apis/
console/.........#project:348986612458, where 348986612458 is
the ProjectID.
c) Click Create.
Take note of the API key value, as you need this to register.
Adding the Push Notification Plugin
Install the Push plugin using the Cordova command line interface.
Prerequisites
Task
When you add the Push plugin to your project, the Settings and Logger plugins are also added
automatically.
1. Add the Push plugin by entering the following at the command prompt, or terminal:
On Windows:
cordova -d plugin add <SDK_HOME>\MobileSDK3\KapselSDK
\plugins\push
On Mac:
cordova -d plugin add ~<SDK_HOME>/MobileSDK3/KapselSDK/
plugins/push
Note: The path you enter to the Kapsel plugin must be the absolute path (not relative
path).
2. (Optional) To see a list of installed plugins in your Cordova project, open a command
prompt or terminal window, navigate to your Cordova project folder, and enter:
cordova plugins
The Cordova command line interface returns a JSON array showing installed plugins, for
example:
[ 'org.apache.cordova.core.camera',
'org.apache.cordova.core.device-motion',
'org.apache.cordova.core.file' ]
In this example, the Cordova project has the Cordova core Camera, Accelerator (devicemotion), and File plugins installed.
3. Modify the files in the www folder for the project as necessary, then copy them to the
platform directories by running:
Kapsel Development
215
Kapsel Development
cordova -d prepare android
cordova -d prepare ios
4. Use the Android IDE or Xcode to deploy and run the project.
Note: If you are using an iOS device, remember to add the "clienthubEntitlements" to the
Keychain Groups in the Entitlement section in Xcode.
Configuring Push on SAP Mobile Platform Server
You must explicitly register the application connection using the Management Cockpit.
1. Start the Management Cockpit.
2. Select Applications, and click New.
3. In the New Application window, enter values.
Field
Value
ID
Name
Application name.
Vendor
Version
Type
Application type.
Native native iOS and Android applications.
Hybrid container-based applications, such as Kapsel.
Agentry metadata-driven applications, such as Agentry.
Application configuration options differ depending on your selection.
Description
216
Kapsel Development
4. (Optional) Configure push notifications for each device type supported.
Apple Push Notifications
Configure Apple Push Notifications for the selected application, to enable client applications
to receive APNS notifications.
1. From Management Cockpit, select Applications > Push.
2. Under Apple, select APNS endpoint. "None" is the default endpoint value for all the
applications.
3. Select Sandbox to configure APNS in a development and testing environment, or
Production to configure APNS in a production environment.
a) Click Browse to navigate to the certificate file.
b) Select the file, and click Open.
c) Enter a valid password.
Note: The default URL is for a production environment; for a development and testing
environment, change the URL to gateway.sandbox.push.apple.com.
4. (Optional) Configure push notifications for each device type supported.
Testing Push Notifications
Test the push and settings plugins.
1. Open the project in your development IDE.
2. Build and run the project.
3. Send a REST request to send a notification to the Kapsel app.
Sample Application for Android
You can use this code to test the Push and Settings APIs on Android.
Make sure you examine the code carefully and make the necessary changes as explained in the
comments.
<html>
<head>
<script src="cordova.js"></script>
<script>
applicationContext = null;
appId = "bobapp2"; //Place your application id here
smpURL = null;
function init() {
// Optional initial connection context
var context = {
"serverHost": "machine_name.com", //Place your SMP
3.0 server name here
"https": "false",
Kapsel Development
217
Kapsel Development
"serverPort": "80",
"user": "smpAdmin", //Place your user name for the
OData Endpoint here
"password": "s3pAdmin", //Place your password for
the OData Endpoint here
"communicatorId": "REST",
"passcode": "password",
"unlockPasscode": "password"
};
sap.Logon.init(logonSuccessCallback, function()
{ alert("Logon Failed"); }, appId, context, sap.Logon.IabUi);
sap.Logger.setLogLevel(sap.Logger.DEBUG);
}
function register() {
try {
sap.Logon.registerOrUnlock(logonSuccessCallback,
errorCallback);
}
catch (e) {
alert("Problem with register");
}
}
function unRegister() {
try {
sap.Logon.core.deleteRegistration(logonUnregisterSuccessCallback,
errorCallback);
}
catch (e) {
alert("problem with unregister");
}
}
function logonSuccessCallback(result) {
console.log("logonSuccessCallback " +
JSON.stringify(result));
if (result) { //calling registerOrUnlock returns null
the second time it is called. Possible bug.
applicationContext = result;
smpURL = applicationContext.applicationEndpointURL;
console.log(smpURL);
if (smpURL.charAt(smpURL.length - 1) == "/") {
smpURL = smpURL.substring(0,
applicationContext.applicationEndpointURL.length - 1);
}
console.log(smpURL);
smpURL = smpURL.substring(0,
smpURL.lastIndexOf("/"));
console.log(smpURL);
}
}
function logonUnregisterSuccessCallback(result) {
console.log("logonUnregisterSuccessCallback " +
218
Kapsel Development
JSON.stringify(result));
applicationContext = null;
}
function errorCallback(e) {
alert("An error occurred");
alert(JSON.stringify(e));
}
function registerForPush() {
var nTypes = sap.Push.notificationType.SOUNDS |
sap.Push.notificationType.ALERT | sap.Push.notificationType.BADGE;
sap.Push.registerForNotificationTypes(nTypes,
regSuccess, regFailure, proccessNotification, "186452565698"); //
GCM Sender ID, null for APNS
}
function unregisterForPush() {
var nTypes = sap.Push.notificationType.SOUNDS |
sap.Push.notificationType.ALERT;
sap.Push.unregisterForNotificationTypes(unregCallback);
}
function regSuccess(mesg) {
alert("Successfully registered"+mesg);
}
function regFailure(errorInfo) {
alert("Failed to register");
alert(JSON.stringify(errorInfo));
console.log("Error while registering.
JSON.stringify(errorInfo));
}
" +
function unregCallback(msg) {
alert("In unregCallback with params");
console.log("Unregistered" + JSON.stringify(msg));
}
function unregCallback() {
alert("In unregCallback with no params");
}
function proccessNotification(notification) {
console.log("Received a notifcation: " +
JSON.stringify(notification));
}
function proccessMissedNotification(notification) {
alert("In processMissedNotification");
console.log("In processMissedNotification");
console.log("Received a missed notifcation: " +
JSON.stringify(notification));
}
Kapsel Development
219
Kapsel Development
function checkForNotification(notification) {
sap.Push.checkForNotification(proccessMissedNotification);
}
function showRegistrationInfo() {
xmlhttp = new XMLHttpRequest();
var url = smpURL + "/odata/applications/latest/" +
appId + "/Connections('" +
applicationContext.applicationConnectionId + "')";
alert(url);
xmlhttp.open("GET", url, false);
xmlhttp.setRequestHeader("X-SMP-APPCID",
applicationContext.applicationConnectionId);
xmlhttp.send();
var responseText = xmlhttp.responseText;
alert(responseText);
console.log(responseText);
}
function getBadgeCallback(data) {
alert("The badge number is : "+ data["badgecount"]);
}
function getBadgeNum(){
if(device.platform == "Android"){
alert("badge number is iOS only!");
return;
}
sap.Push.getBadgeNumber(getBadgeCallback);
}
function badgeCallBack(msg){
alert("Set badget number : " + msg);
}
function setBadgeNum(){
if(device.platform == "Android"){
alert("badge number is iOS only!");
return;
}
sap.Push.setBadgeNumber(10, badgeCallBack);
}
function resetBadgeCallback(msg){
alert("Reset badge number : " + msg);
}
function resetBadgeNum(){
if(device.platform == "Android"){
220
Kapsel Development
alert("badge number is iOS only!");
return;
}
sap.Push.resetBadge(resetBadgeCallback);
}
document.addEventListener("deviceready", init, false);
</script>
</head>
<body>
<h1>Push</h1>
<button onclick="registerForPush()">Register For Push</
button><br><br>
<button onclick="unregisterForPush()">Unregister For Push</
button><br><br>
<button onclick="checkForNotification()">Check for
Notification</button><br><br>
<button onclick="showRegistrationInfo()">Registration Info</
button><br><br>
<button onclick="getBadgeNum()">Get Badge Number</
button><br><br>
<button onclick="setBadgeNum()">Set Badge Number</
button><br><br>
<button onclick="resetBadgeNum()">Reset Badge Number to 0</
button><br>
</body>
</html>
Note: Applications built in SAP Mobile Platform 3.0 and later should adopt the header format
X-SMP-XXX. To maintain backward compatibility, applications built in earlier versions can
continue to use the header format X-SUP-XXX. However, X-SUP-XXXheaders will be
removed future releases.
Generic header
The generic HTTP header is used in the HTTP request to send any notification type such as
APNS, GCM, Blackberry, or WNS.
Header format for notification data in SAP Mobile Platform 3.x and later:
<X-SMP-DATA>
APNS-specific headers
Use these APNS-specific HTTP headers to send APNS notifications via SAP Mobile
Platform:
Kapsel Development
221
Kapsel Development
Header Structure (SAP Mo- Consists of
bile Platform and later)
222
<X-SMP-APNS-ALERT>
A JSON document. You can use this header or other individual headers listed in this table.
<X-SMP-APNS-ALERTBODY>
<X-SMP-APNS-ALERTACTION-LOC-KEY>
<X-SMP-APNS-ALERTLOC-KEY>
<X-SMP-APNS-ALERTLOC-ARGS>
<X-SMP-APNS-ALERTLAUNCH-IMAGE>
<X-SMP-APNS-BADGE>
<X-SMP-APNS-SOUND>
<X-SMP-APNS-DATA>
For additional information about APNS headers, see the Apple Web site: http://
developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/
RemoteNotificationsPG/ApplePushService/ApplePushService.html.
GCM-specific headers
Use these GCM-specific HTTP headers to send GCM notifications:
Kapsel Development
Header Structure (SAP Mobile Plat- Consists of
form and later)
<X-SMP-GCM-COLLAPSEKEY >
An arbitrary string (such as "Updates Available") that collapses a group of like messages
when the device is offline, so that only the last
message is sent to the client.
Note: If you do not include this header, the
default value "Updates Available, is used
<X-SMP-GCM-DATA>
<X-SMP-GCM-DELAYWHILEIDLE>
<X-SMP-GCM-TIMETOLIVE>
For additional information about GCM headers, see the Android Web site: http://
developer.android.com/guide/google/gcm/gcm.html#send-msg.
BES/BIS-specific header
Use the BlackBerry-specific HTTP header to send BES/BIS notifications:
<x-sup-rim-data> or <X-SMP-RIM-DATA>
<X-SMP-WNS-DATA>
Send payload data to the device as raw notification. Payload data may also be a binary data
encoded as a Base64-encoded string. Size
should not exceed 5KB.
<X-SMP-WNS-ALERT>
<X-SMP-WNS-BADGE>
Kapsel Development
223
Kapsel Development
Request Header Structure
Consists of
<X-SMP-MPNS-DATA>
<X-SMP-MPNS-ALERT>
<X-SMP-MPNS-BADGE>
Google Cloud Messaging for Android (GCM) and Apple Push Notification Service
(APNS).
A notification can be sent to a device registered with an application through a rest call at
http://SMP_3.0_SERVER:8080/Notifications/
application_registration_id
To add the Push plugin to your project, use the following command:
cordova plugin add <path to directory containing Kapsel plugins>\push
224
Kapsel Development
To remove the Push plugin from your project, use the following command:
cordova plugin rm com.sap.mp.cordova.plugins.push
Methods
Name
Description
unregisterForNotificationTypes( callback ) on
page 229
Type Definitions
Name
Description
Source
Kapsel Development
225
Kapsel Development
Parameters
Name
Type
Description
callback
function
The callback function that receives the notification.The callback function will receive a
string as it's argument. This
string will contain the notification message send from the
server intact.
Example
function processBackgroudMessage(mesg){
}
function checkBackgroundNotification() {
sap.Push.checkForNotification(processBackgroudMessage);
}
document.addEventListener("onSapLogonSuccess",
checkBackgroundNotification, false);
document.addEventListener("onSapResumeSuccess",
checkBackgroundNotification, false);
Source
226
Name
Type
Description
callback
function
Kapsel Development
Example
function getBadgeNumCallback(data) { badgecount =
data["badgecount"];}
sap.Push.getBadgeNumber(getBadgeNumCallback);
Source
Type
types
string
Types of notifications
the application wants
to receive.The different
types of notifications
are expressed in
<code>notificationType</code> Notificaion types allowed are
Disable all notifications (NONE: 0), Set
badge count on app
icon (BADGE: 1), Play
sounds on receiving
notification
(SOUNDS: 2) and
Show alert on receiving
notification (ALERT:
4).
successCallback
string
Kapsel Development
Argument
Description
227
Kapsel Development
errorCallback
string
notificationlistenerfunc
string
senderId
string
(optional)
Example
regid = "211112269206";
function registerSuccess(mesg){}
function registerFailure(mesg) {}
function ProcessNotification(mesg){}
sap.Push.registerForNotificationTypes(sap.Push.notificationType.bad
ge | sap.Push.notificationType.sound |
sap.Push.notificationType.alert, registerSuccess, registerFailure,
ProcessNotification, regid);
Source
228
Name
Type
Description
callback
function
Kapsel Development
Example
function badgeCallback(mesg){}
sap.Push.resetBadge(badgeCallback);
Source
Type
Description
number
number
callback
function
Example
function badgeCallback(mesg){}
badgenum = 10;
sap.Push.setBadgeNumber(badgenum, badgeCallback);
Source
Kapsel Development
229
Kapsel Development
Parameters
Name
Type
Description
callback
function
Example
function unregCallback(mesg){}
sap.Push.unregisterForNotificationTypes(unregCallback);
Source
Type
Argument
Description
devtok
string
(optional)
Example
function callback(mesg) {}
devToken ="123123213213";//sample device token
sap.Push.updateWithDeviceToken(devToken, callback);
Source
230
Kapsel Development
push.js
1
// ${project.version}
3
4
/**
5
the
6
* <a href="http://developer.android.com/google/gcm/
index.html">Google Cloud Messaging for Android (GCM)</a>
7
* and
8
* <a href="http://developer.apple.com/library/mac/
documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/
Chapters/ApplePushService.html#//apple_ref/doc/uid/TP40008194CH100-SW9">Apple Push Notification Service (APNS)</a>.
9
* <br/><br/>
10
* A notification can be sent to a device registered with an
application through a
11
* rest call at <pre>http://SMP_3.0_SERVER:8080/
Notifications/application_registration_id</pre>
12
* <br/><br/>
13
14
15
* <a href="http://cordova.apache.org/docs/en/edge/
guide_cli_index.md.html#The%20Command-line%20Interface">Cordova
CLI</a>.<br/>
16
* <br/>
17
* To add the Push plugin to your project, use the following
command:<br/>
18
* cordova plugin add <path to directory containing Kapsel
plugins>\push<br/>
19
* <br/>
20
* To remove the Push plugin from your project, use the
following command:<br/>
21
22
* <br/>
23
Kapsel Development
231
Kapsel Development
24
* @namespace
25
* @alias Push
26
* @memberof sap
27
*/
28
29
module.exports = {
30
31
32
33
/**
34
* Helper method for handling failure callbacks. It is
configured as a failure callback in <code> call_native() </code>
35
36
37
38
39
* @private
40
* @name failure
41
* @function
42
*/
43
44
45
sap.Logger.debug("Javascript Callback Error: " +
msg,"PUSHJS",function(m){},function(m){});
46
47
},
48
49
232
/**
50
51
52
53
plugin
Kapsel Development
54
55
* @private
56
* @name call_native
57
* @function
58
*/
59
60
61
if(arguments.length == 2) {
62
args = []
63
64
ret = exec(
65
callback,
capture is successful */
/**
66
sap.Push.failure,
signature capture encounters an error */
67
'SMPPushPlugin',
run "PushNotificationPlugin" */
/**
68
name,
action to perform */
/**
69
the plugin */
70
71
args);
/**
List of arguments to
return ret;
},
72
73
/**
74
75
76
not.
77
* @private
78
* @name isPlatformIOS
79
* @function
80
*/
81
isPlatformIOS: function () {
Kapsel Development
233
Kapsel Development
82
return device.platform == "iPhone" || device.platform ==
"iPad" || device.platform == "iPod touch" || device.platform ==
"iOS"
83
84
},
/**
85
* Function called by the application to get connection
information.
86
87
* @param {string} [types] Types of notifications the
application wants to receive. The different types of notifications
are expressed in <code>notificationType</code>
88
* @param {string} [successCB] Success callback to call
when registration is successful.
89
* @param {string} [errorCB] Error callback to call when
registration attempt fails.
90
* @private
91
* @memberof sap.Push
92
* @function getConnectionSettings
93
* @example
94
* sap.Push.getConnectionSettings(function(){
95
* sap.Logger.debug("getting Connection
Settings","PUSHJS",function(m){},function(m){});
96
97
* sap.Push.registerForNotification(types,
successCallback, errorCallback, notificationListenerFunc,
senderId );
98
99
**/
getConnectionSettings : function (successCB, errorCB) {
100
101
102
if (sap.Settings.isInitialized == true)
103
104
105
successCB();
106
107
234
} else {
Kapsel Development
108
sap.Settings.isInitialized = true;
109
var pd ="";
110
sap.Logon.unlock(function (connectionInfo) {
111
var userName =
connectionInfo["registrationContext"]["user"];
112
var password =
connectionInfo["registrationContext"]["password"];
113
var applicationConnectionId =
connectionInfo["applicationConnectionId"];
114
var securityConfig =
connectionInfo["registrationContext"]["securityConfig"];
115
var endpoint =
connectionInfo["applicationEndpointURL"];
116
117
endpoint.split("/");
var splitendpoint =
118
"https:")
if (splitendpoint[0] ==
119
120
keySSLEnabled="true";
121
122
if (securityConfig == null) {
123
securityConfig = "";
124
125
126
127
pd = appId+userName+password;
128
sap.Settings.store = new
sap.EncryptedStorage("SettingsStore", pd);
129
connectionData = {
130
"keyMAFLogonOperationContextConnectionData": {
131
"keyMAFLogonConnectionDataApplicationSettings":
132
133
"DeviceType":device.platform,
Kapsel Development
235
Kapsel Development
134
"DeviceModel":device.model,
135
"ApplicationConnectionId":applicationConnectionId
136
},
137
"keyMAFLogonConnectionDataBaseURL":burl
138
},
139
"keyMAFLogonOperationContextApplicationId":appId,
140
"keyMAFLogonOperationContextBackendUserName":userName,
141
"keyMAFLogonOperationContextBackendPassword":password,
142
"keyMAFLogonOperationContextSecurityConfig":securityConfig,
143
"keySSLEnabled":keySSLEnabled
144
};
145
sap.Settings.start(connectionData,
146
{
function(mesg)
147
sap.Settings.isInitialized = true;
148
sap.Logger.debug("Setting Exchange is succesful
","SETTINGSJS",function(m){},function(m){});
149
successCB();
150
151
{
},
function(mesg)
152
sap.Logger.debug("Setting Exchange failed" +
mesg,"SETTINGSJS",function(m){},function(m){});
153
sap.Settings.isInitialized = false;
154
errorCB();
236
Kapsel Development
155
});
156
157
, function () {
158
failed");
console.log("unlock
159
sap.Logger.debug("unlock failed
","SETTINGSJS",function(m){},function(m){});
160
161
);
162
163
164
165
166
},
167
168
/**
169
* Function called by the application to register the
notification types to receive.
170
171
* @param {string} [types] Types of notifications the
application wants to receive. The different types of notifications
are expressed in <code>notificationType</code>
172
* @param {string} [successCallback] Success callback to
call when registration is successful.
173
* @param {string} [errorCallback] Error callback to call
when registration attempt fails.
174
* @param {string} [notificationlistenerfunc] The function
that receives the notification for processing by the application.
175
* @param {string} [senderId] The sender ID that is used
for GCM registration. For other platforms it is null.
176
* @private
177
* @memberof sap.Push
178
* @function registerForNotificationTypes
179
* @example
180
* regid = "211112269206";
181
* function registerSuccess(mesg){}
Kapsel Development
237
Kapsel Development
182
* function registerFailure(mesg) {}
183
* function ProcessNotification(mesg){}
184
*
sap.Push.registerForNotificationTypes(sap.Push.notificationType.bad
ge | sap.Push.notificationType.sound |
sap.Push.notificationType.alert, registerSuccess, registerFailure,
ProcessNotification, regid);
185
*/
186
187
registerForNotification: function (types, successCallback,
errorCallback, notificationListenerFunc, senderId ) {
188
if(device.platform == "iPhone" || device.platform ==
"iPad" || device.platform == "iPod touch" || device.platform == "iOS"
|| device.platform == "Android") {
189
sap.Push.RegisterSuccess = successCallback;
190
sap.Push.RegisterFailed
= errorCallback;
191
sap.Push.ProcessNotificationForUser =
notificationListenerFunc;
192
sap.Push.call_native(successCallback,
"registerForNotificationTypes", [types, senderId]);
193
194
195
196
},
197
198
/* Core APIS */
199
200
/**
201
* Function called by the application to register the
notification types to receive.
202
203
* @param {string} types Types of notifications the
application wants to receive. The different types of notifications
are expressed in <code>notificationType</code>
204
*
Notificaion types allowed are Disable all
notifications (NONE: 0), Set badge count on app icon (BADGE: 1), Play
sounds on receiving notification (SOUNDS: 2) and Show alert on
receiving notification (ALERT: 4).
238
Kapsel Development
205
* @param {string} successCallback Success callback to
call when registration is successful.
206
* @param {string} errorCallback Error callback to call
when registration attempt fails.
207
* @param {string} notificationlistenerfunc The function
that receives the notification for processing by the application.
208
* @param {string} [senderId] The sender ID that is used
for GCM registration. For other platforms it is null.
209
* @public
210
* @memberof sap.Push
211
* @function registerForNotificationTypes
212
* @example
213
* regid = "211112269206";
214
* function registerSuccess(mesg){}
215
* function registerFailure(mesg) {}
216
* function ProcessNotification(mesg){}
217
*
sap.Push.registerForNotificationTypes(sap.Push.notificationType.bad
ge | sap.Push.notificationType.sound |
sap.Push.notificationType.alert, registerSuccess, registerFailure,
ProcessNotification, regid);
218
*/
219
registerForNotificationTypes: function (types,
successCallback, errorCallback, notificationListenerFunc, senderId )
{
220
sap.Push.getConnectionSettings(function(){
221
sap.Logger.debug("getting
Connection Settings","PUSHJS",function(m){},function(m){});
222
Settings");
console.log("getting Connection
223
sap.Push.registerForNotification(types, successCallback,
errorCallback, notificationListenerFunc, senderId );
224
},
225
function(){});
226
},
227
228
Kapsel Development
239
Kapsel Development
229
/**
230
* Function called by the application to unregister from
future notifications.
231
232
* @param {function} callback Success callback to call
when deregistration is successful. This callback function will
contain a string with a message. This message is for informative
purposes only.
233
* @public
234
* @memberof sap.Push
235
* @function unregisterForNotificationTypes
236
* @example
237
* function unregCallback(mesg){}
238
*
sap.Push.unregisterForNotificationTypes(unregCallback);
239
*/
240
241
242
if(device.platform == "iPhone" || device.platform ==
"iPad" || device.platform == "iPod touch" || device.platform == "iOS"
|| device.platform == "Android") {
243
sap.Push.call_native(callbak,"unregisterForNotification");
244
245
}
},
246
247
/**
248
* Used to fetch the badge count for the application. This
function is used by iOS only. Other platforms do not have the badge
count concept.
249
250
* @param {function} callback Success callback to call
when to send the badge count. The callback function will contain an
argument in JSON format with the current badge count. Look at the
example for the details on how to use them.
240
251
* @public
252
* @memberof sap.Push
Kapsel Development
253
* @function getBadgeNumber
254
* @example
255
* function getBadgeNumCallback(data) { badgecount =
data["badgecount"];}
256
* sap.Push.getBadgeNumber(getBadgeNumCallback);
257
*/
258
259
getBadgeNumber: function(callback)
{
260
if (sap.Push.isPlatformIOS()) {
261
"getBadgeNumber");
262
263
sap.Push.call_native(callback,
}
},
264
265
/**
266
* Used to set the badge count for the application. This
function is used by iOS only. Other platforms do not have the badge
count concept.
267
268
* @param {number} number The badge count to set for the
application.
269
* @param {function} callback Success callback to call
when to send the badge count. The callback function will contain an
argument in string format. This argument can be used for informative
purposes.
270
* @public
271
* @memberof sap.Push
272
* @function setBadgeNumber
273
* @example
274
* function badgeCallback(mesg){}
275
* badgenum = 10;
276
* sap.Push.setBadgeNumber(badgenum, badgeCallback);
277
*/
278
279
Kapsel Development
241
Kapsel Development
280
[number]);
281
282
sap.Push.call_native(callback, "setBadgeNumber",
}
},
283
284
/**
285
* Used to reset the badge count for the application. This
function is used by iOS only. Other platforms do not have the badge
count concept.
286
287
* @param {function} callback Success callback to call
when the badge count is reset. The callback function will contain an
argument in string format. This argument can be used for informative
purpose.
288
* @public
289
* @memberof sap.Push
290
* @function resetBadge
291
* @example
292
* function badgeCallback(mesg){}
293
* sap.Push.resetBadge(badgeCallback);
294
*/
295
296
if (sap.Push.isPlatformIOS()) {
297
sap.Push.call_native(callback, "resetBadge");
298
299
}
},
300
301
302
303
304
305
/**
306
* This method updates the application with the new device
token in the SAP Mobile Platform server.
307
242
Kapsel Development
308
* @param {string} [devtok] The device token received from
the APNS/GCM device registration.
309
* @public
310
* @callback {function} [callback] The callback function
that is called with the registration result.
311
* @memberof sap.Push
312
* @example
313
* function callback(mesg) {}
314
315
* sap.Push.updateWithDeviceToken(devToken, callback);
316
*/
317
318
updateWithDeviceToken:
319
if (sap.Push.isPlatformIOS() || device.platform ==
"Android" ) {
320
sap.Push.call_native(callback,
"updateWithDeviceToken", [devtok]);
321
322
}
},
323
324
/**
325
* This method checks for any notifications received while
the application was not running in the foreground. You can call
this
326
* function directly or register with an event handler to
be called automatically. It is okay to call this function even if the
device is not yet registered for push notifications.
327
* @param {function} callback The callback function that
receives the notification. The callback function will receive a
string as its argument. This string will contain the notification
message sent from the server, intact.
328
* @memberof sap.Push
329
* @example
330
* function processBackgroudMessage(mesg){
331
332
* }
333
* function checkBackgroundNotification() {
Kapsel Development
243
Kapsel Development
334
*
sap.Push.checkForNotification(processBackgroudMessage);
335
* }
336
* document.addEventListener("onSapLogonSuccess",
checkBackgroundNotification, false);
337
* document.addEventListener("onSapResumeSuccess",
checkBackgroundNotification, false);
338
**/
339
340
checkForNotification: function(callback) {
341
if (sap.Push.isPlatformIOS() || device.platform ==
"Android" ) {
342
sap.Push.call_native(callback,
"checkForNotification");
343
344
}
},
345
346
/**
347
* This is an internal function, which is called when there
is a push notification.
348
* @private
349
**/
350
ProcessNotification: function(message) {
351
if (sap.Push.ProcessNotificationForUser == null )
352
353
354
sap.Logger.debug("Notification listener function is
not registered. Register it by calling
registerForNotificationTypes","PUSHJS",function(m){},function(m)
{});
355
} else {
356
sap.Push.ProcessNotificationForUser(message);
357
358
359
244
}
},
/**
Kapsel Development
360
* This is an internal function, which is automatically
called when the plugin is initialized. Used for Android only.
361
* @private
362
**/
363
initPlugin: function(callback) {
364
if ( device.platform == "Android")
365
366
args = [];
367
exec(
368
callback,
369
function(){ sap.Logger.debug("Plugin
Initialization","PUSHJS",function(m){},function(m){}); } ,
370
'SMPPushPlugin',
371
"initPlugin",
372
args);
373
374
}
}
375
376
};
377
378
379
/**
380
381
*/
382
module.exports.RegisterSuccess = null;
383
module.exports.RegisterFailed = null;
384
module.exports.ProcessNotificationForUser = null;
385
/**
386
387
* @enum {number}
388
* @private
389
*/
390
module.exports.notificationType = {
Kapsel Development
245
Kapsel Development
391
392
393
394
395
396
397
398
ALERT: 4
399
};
400
401
402
403
404
document.addEventListener('deviceready',
module.exports.initPlugin, false);
405
406
246
Kapsel Development
Note: On Android, you cannot store more than 1MB for a single key/value pair, as the strings
are encoded in UTF-8, which means the maximum length of a complex string that can be
successfully stored is less than the maximum length of a string with only simple characters
(since simple characters are encoded with a single byte, and complex characters are encoded
with up to 4 bytes).
Adding the EncryptedStorage Plugin
Install the the EncryptedStorage plugin using the Cordova command line interface.
Prerequisites
Task
1. Add the EncryptedStorage plugin by entering the following at the command prompt, or
terminal:
On Windows:
cordova -d plugin add <SDK_HOME>\MobileSDK3\KapselSDK
\plugins\encryptedstorage
On Mac:
cordova -d plugin add ~<SDK_HOME>/MobileSDK3/KapselSDK/
plugins/encryptedstorage
Note: The path you enter to the Kapsel plugin must be the absolute path (not relative
path).
2. (Optional) To see a list of installed plugins in your Cordova project, open a command
prompt or terminal window, navigate to your Cordova project folder, and enter:
cordova plugins
The Cordova command line interface returns a JSON array showing installed plugins, for
example:
[ 'org.apache.cordova.core.camera',
'org.apache.cordova.core.device-motion',
'org.apache.cordova.core.file' ]
In this example, the Cordova project has the Cordova core Camera, Accelerator (devicemotion), and File plugins installed.
3. Modify the files in the www folder for the project as necessary, then copy them to the
platform directories by running:
Kapsel Development
247
Kapsel Development
cordova -d prepare android
cordova -d prepare ios
4. Use the Android IDE or Xcode to deploy and run the project.
Note: If you are using an iOS device, remember to add the "clienthubEntitlements" to the
Keychain Groups in the Entitlement section in Xcode.
Kapsel EncryptedStorage API Reference
The Kapsel EncryptedStorage API Reference provides usage information for
EncryptedStorage API classes and methods, as well as provides sample source code.
EncryptedStorage namespace
The EncryptedStorage class is used as a secure local store.
The EncryptedStorage API is based on the W3C web storage API, but has two major
differences: it is asynchronous, and it has a constructor with a password.
Note: There is a security flaw on some versions of Android with the Pseudo Random Number
Generation. The first time the native code of this plugin runs it applies the fix for this issue.
However, the fix needs to be applied before any use of Java Cryptography Architecture
primitives. Therefore, it is a good idea to run this plugin (call a function that has a native
component: length, key, getItem, setItem, removeItem, clear) before using any other securityrelated plugin, to protect yourself against the possibility that the other plugin does not apply
this fix. No other Kapsel plugins are affected, so you need not do this on their behalf. For more
details about the security flaw, see http://android-developers.blogspot.com/2013/08/some-
securerandom-thoughts.html
To add the EncryptedStorage plugin to your project, use the following command:
cordova plugin add <path to directory containing Kapsel plugins>\encryptedstorage
248
Kapsel Development
To remove the EncryptedStorage plugin from your project, use the following command:
cordova plugin rm com.sap.mp.cordova.plugins.encryptedstorage
Members
Name
Description
COMPLEX_STRING_MAXIMUM_LENGTH
on page 250
ERROR_INVALID_PARAMETER on page
251
SIMPLE_STRING_MAXIMUM_LENGTH on
page 251
Methods
Name
Description
length( successCallback, errorCallback ) on page This function gets the length of the store.
254
removeItem( key, successCallback, errorCallback ) on page 255
Kapsel Development
249
Kapsel Development
Type Definitions
Name
Description
Source
250
Kapsel Development
ERROR_GREATER_THAN_MAXIMUM_SIZE member
This error indicates that the string was too large to store.
Only applies to Android. For iOS, no hard limit is imposed, but be aware of device memory
constraints.
Syntax
<constant> ERROR_GREATER_THAN_MAXIMUM_SIZE
Source
Kapsel Development
251
Kapsel Development
Source
Type
Description
successCallback
errorCallback
If there is an error, the errorCallback is invoked with an ErrorInfo object as the parameter.
Example
var store = new sap.EncryptedStorage("storeName", "storePassword");
var successCallback = function() {
alert("Store cleared!");
}
var errorCallback = function(error) {
alert("An error occurred: " + JSON.stringify();
}
store.clear(successCallback, errorCallback);
Source
252
Kapsel Development
Parameters
Name
Type
Description
key
String
successCallback
sap.EncryptedStorage~getItemSuccessCallback on page
258
errorCallback
If there is an error, the errorCallback is invoked with an ErrorInfo object as the parameter.
Example
var store = new sap.EncryptedStorage("storeName", "storePassword");
var successCallback = function(value) {
alert("Value is " + value);
}
var errorCallback = function(error) {
alert("An error occurred: " + JSON.stringify(error));
}
store.getItem("theKey", successCallback, errorCallback);
Source
Kapsel Development
Type
Description
253
Kapsel Development
index
number
successCallback
errorCallback
If there is an error, the errorCallback is invoked with an ErrorInfo object as the parameter.
Example
// This example shows how to get the key for the last item.
var store = new sap.EncryptedStorage("storeName", "storePassword");
var errorCallback = function( error ){
alert("An error occurred: " + JSON.stringify(error));
}
var keySuccessCallback = function(key) {
alert("Last key is " + key);
}
var lengthSuccessCallback = function(length) {
store.key(length - 1, keySuccessCallback, errorCallback);
}
store.length(lengthSuccessCallback, errorCallback);
Source
254
Type
Description
Kapsel Development
successCallback
errorCallback
If there is an error, the errorCallback is invoked with an ErrorInfo object as the parameter.
Example
var store = new sap.EncryptedStorage("storeName", "storePassword");
var successCallback = function(length) {
alert("Length is " + length);
}
var errorCallback = function(error) {
alert("An error occurred: " + JSON.stringify(error));
}
store.length(successCallback, errorCallback);
Source
Type
Description
key
String
successCallback
errorCallback
If there is an error, the errorCallback is invoked with an ErrorInfo object as the parameter.
Kapsel Development
255
Kapsel Development
Example
var store = new sap.EncryptedStorage("storeName", "storePassword");
var successCallback = function() {
alert("Value removed");
}
var errorCallback = function(error) {
alert("An error occurred: " + JSON.stringify(error));
}
store.removeItem("somekey", successCallback, errorCallback);
Source
256
Name
Type
Description
key
String
value
String
successCallback
Kapsel Development
errorCallback
If there is an error, the errorCallback is invoked with an ErrorInfo object as the parameter.
Example
var store = new sap.EncryptedStorage("storeName", "storePassword");
var successCallback = function() {
alert("Item has been set.");
}
var errorCallback = function(error) {
alert("An error occurred: " + JSON.stringify(error));
}
store.setItem("somekey", "somevalue", successCallback,
errorCallback);
Source
Type
Description
errorCode
number
sap.EncryptedStorage#ERROR_UNKNOWN on page
251, sap.EncryptedStorage#ERROR_INVALID_PARAMETER on page 251,
sap.EncryptedStorage#ERROR_BAD_PASSWORD on
page 250, or sap.EncryptedStorage#ERROR_GREATER_THAN_MAXIMUM_SIZE on page 251.
Example
function errorCallback(errCode) {
//Set the default error message. Used if an invalid code is passed
to the
Kapsel Development
257
Kapsel Development
//function (just in case) but also to cover the
//sap.EncryptedStorage.ERROR_UNKNOWN case as well.
var msg = "Unkown Error";
switch (errCode) {
case sap.EncryptedStorage.ERROR_INVALID_PARAMETER:
msg = "Invalid parameter passed to method";
break;
case sap.EncryptedStorage.ERROR_BAD_PASSWORD :
msg = "Incorrect password";
break;
case sap.EncryptedStorage.ERROR_GREATER_THAN_MAXIMUM_SIZE:
msg = "Item (string) value too large to write to store";
break;
};
//Write the error to the log
console.error(msg);
//Let the user know what happened
navigator.notification.alert(msg, null, "EncryptedStorage Error",
"OK");
};
Source
encryptedstorage.js, line 328 on page 272.
getItemSuccessCallback( value ) type
Callback function that is invoked on a successful call to EncryptedStorage.getItem. If the
returned value is null, that means the key passed to EncryptedStorage.getItem did not exist.
Syntax
getItemSuccessCallback( value )
Parameters
Name
Type
Description
value
String
Source
258
Kapsel Development
Syntax
keySuccessCallback( key )
Parameters
Name
Type
Description
key
String
Source
Type
Description
length
number
Source
Kapsel Development
259
Kapsel Development
encryptedstorage.js
1
// ${project.version}
exec = require("cordova/exec");
4
5
/**
6
store.
7
* W3C Web storage API, but has two major differences--it is
asynchronous, and it has a constructor with
8
* a password.<br/>
* <br/>
10
* <b>Note:</b> There is a security flaw on some versions of
Android with the Pseudo Random Number Generation.
11
* The first time the native code of this plugin runs it
applies the fix for this issue. However, the
12
* fix needs to be applied before any use of Java Cryptography
Architecture primitives. Therefore, it
13
* is a good idea to run this plugin, for example, call a
function that has a native component such as length, key, getItem,
14
* setItem, removeItem, or clear, before using any other
security-related plugin, to protect yourself
15
* against the possibility that the other plugin does not
apply this fix. This issue affects only the EncryptedStorage
plugin,
16
*so you need not do this for other Kapsel plugins.
details about the security flaw, see
For more
17
* <a href="http://android-developers.blogspot.com/2013/08/
some-securerandom-thoughts.html">
18
* http://android-developers.blogspot.com/2013/08/somesecurerandom-thoughts.html</a><br/>
19
20
>
21
* <br/>
* <b>Adding and Removing the EncryptedStorage Plugin</b><br/
* Add or remove the EncryptedStorage plugin by using the
22
* <a href="http://cordova.apache.org/docs/en/edge/
guide_cli_index.md.html#The%20Command-line%20Interface">Cordova
CLI</a>.<br/>
260
Kapsel Development
23
* <br/>
24
* To add the EncryptedStorage plugin to your project, use the
following command:<br/>
25
* cordova plugin add <path to directory containing Kapsel
plugins>\encryptedstorage<br/>
26
* <br/>
27
* To remove the EncryptedStorage plugin from your project,
use the following command:<br/>
28
* cordova plugin rm
com.sap.mp.cordova.plugins.encryptedstorage
29
* @namespace
30
* @alias EncryptedStorage
31
* @memberof sap
32
* @param {String} storeName The name of the store to create.
All stores with different names
33
* act independently, while stores with the same name (and
same password) act as the same store.
34
35
create.
36
37
*/
38
39
// private variables
40
41
42
43
44
// privileged functions
45
46
/**
47
* This function gets the length of the store.
of a store
48
store.
Kapsel Development
The length
261
Kapsel Development
49
* @param {sap.EncryptedStorage~lengthSuccessCallback}
successCallback If successful,
50
store as
51
52
* @param {sap.EncryptedStorage~errorCallback}
errorCallback If there is an error,
53
* the errorCallback is invoked with an ErrorInfo object as
the parameter.
54
* @memberof sap.EncryptedStorage
55
* @function length
56
* @instance
57
* @example
58
* var store = new sap.EncryptedStorage("storeName",
"storePassword");
59
60
61
* }
62
63
*
alert("An error occurred: " +
JSON.stringify(error));
64
* }
65
* store.length(successCallback, errorCallback);
66
*/
67
{
68
69
argscheck.checkArgs('FF',
'EncryptedStorage.length', arguments);
70
}catch(ex){
71
errorCallback(this.ERROR_INVALID_PARAMETER);
72
return;
73
74
75
cordova.exec(successCallback, errorCallback,
"EncryptedStorage",
262
Kapsel Development
76
77
78
79
/**
80
index.
81
* @param {number} index The index of the store for which
to get the key.
82
* Valid indices are integers from zero (the first index),
up to, but not including,
83
* the length of the store.
then the success
84
85
* @param {sap.EncryptedStorage~keySuccessCallback}
successCallback If successful,
86
parameter.
87
* @param {sap.EncryptedStorage~errorCallback}
errorCallback If there is an error,
88
* the errorCallback is invoked with an ErrorInfo object as
the parameter.
89
* @memberof sap.EncryptedStorage
90
* @function key
91
* @instance
92
* @example
93
item.
* // This example shows how to get the key for the last
94
* var store = new sap.EncryptedStorage("storeName",
"storePassword");
95
96
*
alert("An error occurred: " +
JSON.stringify(error));
97
* }
98
99
100
* }
101
Kapsel Development
263
Kapsel Development
102
*
errorCallback);
store.key(length - 1, keySuccessCallback,
103
* }
104
* store.length(lengthSuccessCallback, errorCallback);
105
*/
106
this.key = function (index, successCallback,
errorCallback) {
107
try{
108
arguments);
argscheck.checkArgs('NFF', 'EncryptedStorage.key',
109
}catch(ex){
110
errorCallback(this.ERROR_INVALID_PARAMETER);
111
return;
112
113
114
cordova.exec(successCallback, errorCallback,
"EncryptedStorage",
115
116
117
118
119
key.
/**
* This function gets the value corresponding to the given
If there is no
120
* item with the given key, then the success callback is
invoked with null as
121
* the parameter.
122
* @param {String} key The key of the item for which to get
the value. If null or undefined is
123
124
* @param {sap.EncryptedStorage~getItemSuccessCallback}
successCallback If successful,
125
* the successCallback is invoked with the value as the
parameter (or null if the key
126
127
* @param {sap.EncryptedStorage~errorCallback}
errorCallback If there is an error,
264
Kapsel Development
128
* the errorCallback is invoked with an ErrorInfo object
as the parameter.
129
* @memberof sap.EncryptedStorage
130
* @function getItem
131
* @instance
132
* @example
133
* var store = new sap.EncryptedStorage("storeName",
"storePassword");
134
135
136
* }
137
138
*
alert("An error occurred: " +
JSON.stringify(error));
139
* }
140
* store.getItem("theKey", successCallback,
errorCallback);
141
*/
142
this.getItem = function (key, successCallback,
errorCallback) {
143
try{
144
argscheck.checkArgs('SFF',
'EncryptedStorage.getItem', arguments);
145
}catch(ex){
146
errorCallback(this.ERROR_INVALID_PARAMETER);
147
return;
148
149
150
cordova.exec(successCallback, errorCallback,
"EncryptedStorage",
151
key]);
152
153
154
/**
Kapsel Development
265
Kapsel Development
155
value.
156
* the given key, a new item is created. If an item does
exist with the
157
* the given key, its value is overwritten with the given
value.<br/>
158
* <br/>
159
* Note: On Android there is a size limit on the string to
be stored. See
160
* {@link
sap.EncryptedStorage#SIMPLE_STRING_MAXIMUM_LENGTH} and {@link
sap.EncryptedStorage#COMPLEX_STRING_MAXIMUM_LENGTH}
161
162
* @param {String} key The key of the item to set.
or undefined is passed,
163
If null
* "null" is used.
164
* @param {String} value The value of the item to set.
null or undefined is passed,
165
If
* "null" is used.
166
* @param {sap.EncryptedStorage~successCallback}
successCallback If successful,
167
168
* @param {sap.EncryptedStorage~errorCallback}
errorCallback If there is an error,
169
* the errorCallback is invoked with an ErrorInfo object
as the parameter.
170
* @memberof sap.EncryptedStorage
171
* @function setItem
172
* @instance
173
* @example
174
* var store = new sap.EncryptedStorage("storeName",
"storePassword");
266
175
176
177
* }
178
Kapsel Development
179
*
alert("An error occurred: " +
JSON.stringify(error));
180
* }
181
* store.setItem("somekey", "somevalue", successCallback,
errorCallback);
182
*/
183
this.setItem = function (key, value, successCallback,
errorCallback) {
184
try{
185
argscheck.checkArgs('SSFF',
'EncryptedStorage.setItem', arguments);
186
}catch(ex){
187
errorCallback(this.ERROR_INVALID_PARAMETER);
188
return;
189
190
191
cordova.exec(successCallback, errorCallback,
"EncryptedStorage",
192
value]);
193
194
195
/**
196
* This function removes the item corresponding to the
given key. If there is no
197
* item with the given key in the first place, that is
still counted as a success.
198
* @param {String} key The key of the item to remove.
null or undefined is
199
If
200
* @param {sap.EncryptedStorage~successCallback}
successCallback If successful,
201
202
* @param {sap.EncryptedStorage~errorCallback}
errorCallback If there is an error,
203
* the errorCallback is invoked with an ErrorInfo object
as the parameter.
Kapsel Development
267
Kapsel Development
204
* @memberof sap.EncryptedStorage
205
* @function removeItem
206
* @instance
207
* @example
208
* var store = new sap.EncryptedStorage("storeName",
"storePassword");
209
210
211
* }
212
alert("Value removed");
213
*
alert("An error occurred: " +
JSON.stringify(error));
214
* }
215
* store.removeItem("somekey", successCallback,
errorCallback);
216
*/
217
this.removeItem = function (key, successCallback,
errorCallback) {
218
try{
219
argscheck.checkArgs('SFF',
'EncryptedStorage.removeItem', arguments);
220
}catch(ex){
221
errorCallback(this.ERROR_INVALID_PARAMETER);
222
return;
223
224
225
cordova.exec(successCallback, errorCallback,
"EncryptedStorage",
226
key]);
227
228
229
230
are no
268
/**
* This function removes all items from the store. If there
Kapsel Development
231
success.
232
* @param {sap.EncryptedStorage~successCallback}
successCallback If successful,
233
234
* @param {sap.EncryptedStorage~errorCallback}
errorCallback If there is an error,
235
* the errorCallback is invoked with an ErrorInfo object
as the parameter.
236
* @memberof sap.EncryptedStorage
237
* @function clear
238
* @instance
239
* @example
240
* var store = new sap.EncryptedStorage("storeName",
"storePassword");
241
242
243
* }
244
245
246
* }
247
* store.clear(successCallback, errorCallback);
248
*/
249
{
alert("Store cleared!");
250
try{
251
argscheck.checkArgs('FF',
'EncryptedStorage.clear', arguments);
252
}catch(ex){
253
errorCallback(this.ERROR_INVALID_PARAMETER);
254
return;
255
256
257
cordova.exec(successCallback, errorCallback,
"EncryptedStorage",
Kapsel Development
269
Kapsel Development
258
259
260
}
};
261
262
// Error codes
263
/**
264
265
* @memberof sap.EncryptedStorage
266
* @name sap.EncryptedStorage#ERROR_UNKNOWN
267
* @constant
268
*/
269
EncryptedStorage.prototype.ERROR_UNKNOWN = 0;
270
/**
271
* This error code indicates an invalid parameter was
provided.
272
273
* @memberof sap.EncryptedStorage
274
* @name sap.EncryptedStorage#ERROR_INVALID_PARAMETER
275
* @constant
276
*/
277
EncryptedStorage.prototype.ERROR_INVALID_PARAMETER = 1;
278
/**
279
* This error code indicates that the operation failed due to
an incorrect password. The password is
280
281
* @memberof sap.EncryptedStorage
282
* @name sap.EncryptedStorage#ERROR_BAD_PASSWORD
283
* @constant
284
*/
285
EncryptedStorage.prototype.ERROR_BAD_PASSWORD = 2;
286
/**
287
* This error indicates that the string was too large to
store. This error applies only to Android.
270
Kapsel Development
288
* For iOS, no hard limit is imposed, but be aware of device
memory constraints.
289
* @memberof sap.EncryptedStorage
290
* @name
sap.EncryptedStorage#ERROR_GREATER_THAN_MAXIMUM_SIZE
291
* @constant
292
*/
293
3;
294
EncryptedStorage.prototype.ERROR_GREATER_THAN_MAXIMUM_SIZE =
/**
295
* This constant is the length of the largest string that can
successfully be stored on Android. Only if all the
296
* characters in the string are encoded in 1 byte in UTF-8 can
a string actually be this big. Since
297
* characters in UTF-8 can take up to 4 bytes, if you do not
know the contents of a string, the maximum
298
* length that is guaranteed to be successful is {@link
EncryptedStorage.COMPLEX_STRING_MAXIMUM_LENGTH}, which is
299
* {@link EncryptedStorage.SIMPLE_STRING_MAXIMUM_LENGTH}/4.
This size restriction is present only on
300
* Android.
301
* @memberof sap.EncryptedStorage
302
* @name sap.EncryptedStorage#SIMPLE_STRING_MAXIMUM_LENGTH
303
* @constant
304
*/
305
EncryptedStorage.prototype.SIMPLE_STRING_MAXIMUM_LENGTH =
1048542;
306
/**
307
* This constant is the length of the largest string that is
guaranteed to be successfully stored on Android. The
308
* limit depends on how many bytes the string takes up when
encoded with UTF-8 (under which encoding
309
* characters can take up to 4 bytes).
length of a string for which every character
310
* takes all 4 bytes.
on Android.
311
* @memberof sap.EncryptedStorage
Kapsel Development
271
Kapsel Development
312
* @name sap.EncryptedStorage#COMPLEX_STRING_MAXIMUM_LENGTH
313
* @constant
314
*/
315
EncryptedStorage.prototype.COMPLEX_STRING_MAXIMUM_LENGTH =
262135;
316
317
module.exports = EncryptedStorage;
318
319
320
/**
321
* Callback function that is invoked on a successful call to a
function that does
322
323
324
* @callback sap.EncryptedStorage~successCallback
325
*/
326
327
/**
328
* Callback function that is invoked on a successful call to
{@link EncryptedStorage.length}.
329
330
* @callback sap.EncryptedStorage~lengthSuccessCallback
331
332
store.
333
334
335
/**
336
* Callback function that is invoked on a successful call to
{@link EncryptedStorage.key}.
337
* If the key returned is null, that means the index passed to
{@link EncryptedStorage.key} was out of bounds.
272
338
339
* @callback sap.EncryptedStorage~keySuccessCallback
Kapsel Development
340
341
index.
*
* @param {String} key The key corresponding to the given
Will be null if the index passed to
342
343
*/
344
345
/**
346
* Callback function that is invoked on a successful call to
{@link EncryptedStorage.getItem}.
347
* If the returned value is null, that means the key passed to
{@link EncryptedStorage.getItem} did not exist.
348
349
* @callback sap.EncryptedStorage~getItemSuccessCallback
350
351
key.
* @param {String} value The value of the item with the given
Will be null if the key passed to
352
353
*/
354
355
/**
356
357
358
* @callback sap.EncryptedStorage~errorCallback
359
360
* @param {number} errorCode An error code indicating what
went wrong. Will be one of {@link
sap.EncryptedStorage#ERROR_UNKNOWN},
361
* {@link sap.EncryptedStorage#ERROR_INVALID_PARAMETER},
{@link sap.EncryptedStorage#ERROR_BAD_PASSWORD}, or
362
* {@link
sap.EncryptedStorage#ERROR_GREATER_THAN_MAXIMUM_SIZE}.
363
364
* @example
365
* function errorCallback(errCode) {
366
*
//Set the default error message. Used if an invalid code
is passed to the
Kapsel Development
273
Kapsel Development
367
368
369
370
switch (errCode) {
371
case sap.EncryptedStorage.ERROR_INVALID_PARAMETER:
372
373
break;
374
375
376
break;
case sap.EncryptedStorage.ERROR_BAD_PASSWORD :
377
*
case
sap.EncryptedStorage.ERROR_GREATER_THAN_MAXIMUM_SIZE:
378
store";
379
380
};
381
382
console.error(msg);
383
break;
384
*
navigator.notification.alert(msg, null,
"EncryptedStorage Error", "OK");
385
* };
386
*/
274
Kapsel Development
DeviceType, DeviceModel, PushEnabled, and other push-related statuses. The settings also
use the device token that is received during device registration. The server uses this
information to determine whether to send a GCM or APNS push notification.
Adding the Settings Plugin
To install the Settings plugin, use the Cordova command line interface.
Prerequisites
Task
The Settings plugin has dependencies on the Logger plugin, so when you install the Settings
plugin, the Logger plugin is added automatically.
1. Add the Settings plugin by entering the following at the command prompt, or terminal:
On Windows:
cordova -d plugin add <SDK_HOME>\MobileSDK3\KapselSDK
\plugins\settings
On Mac:
cordova -d plugin add ~<SDK_HOME>/MobileSDK3/KapselSDK/
plugins/settings
Note: The path you enter to the Kapsel plugin must be the absolute path (not relative
path).
2. (Optional) To see a list of installed plugins in your Cordova project, open a command
prompt or terminal window, navigate to your Cordova project folder, and enter:
cordova plugins
The Cordova command line interface returns a JSON array showing installed plugins, for
example:
[ 'org.apache.cordova.core.camera',
'org.apache.cordova.core.device-motion',
'org.apache.cordova.core.file' ]
In this example, the Cordova project has the Cordova core Camera, Accelerator (devicemotion), and File plugins installed.
3. Modify the files in the www folder for the project as necessary, then copy them to the
platform directories by running:
cordova -d prepare android
cordova -d prepare ios
Kapsel Development
275
Kapsel Development
4. Use the Android IDE or Xcode to deploy and run the project.
Note: If you are using an iOS device, remember to add the "clienthubEntitlements" to the
Keychain Groups in the Entitlement section in Xcode.
Kapsel Settings API Reference
The Kapsel Settings API Reference provides usage information for Settings API classes and
methods, as well as provides sample source code.
Settings namespace
Provides settings exchange functionality
Methods
Name
Description
Source
Type
Description
connectionData
String
successCallback
function
errorCallback
function
Example
connectionData = {
"keyMAFLogonOperationContextConnectionData": {
"keyMAFLogonConnectionDataApplicationSettings":
{
"DeviceType":device.platform,
276
Kapsel Development
"DeviceModel":device.model,
"ApplicationConnectionId":"yourappconnectionid"
},
"keyMAFLogonConnectionDataBaseURL":"servername:port"
},
"keyMAFLogonOperationContextApplicationId":"yourapplicationid",
"keyMAFLogonOperationContextBackendUserName":"yourusername",
"keyMAFLogonOperationContextBackendPassword":"password",
"keyMAFLogonOperationContextSecurityConfig":"securityConfigName",
"keySSLEnabled":keySSLEnabled
};
sap.Settings.start(connectionData, function(mesg) {
sap.Logger.debug("Setting Exchange
is successful "+mesg,"SMP_SETTINGS_JS",function(m){},function(m){});
},
function(mesg){
sap.Logger.debug("Setting Exchange
failed" + mesg,"SMP_SETTINGS_JS",function(m){},function(m){});
});
Source
settings.js, line 96 on page 281.
Source code
settings.js
1
// ${project.version}
3
4
5
/**
6
* The Kapsel Settings plugin provides settings exchange
functionality.
7
* @namespace
* @alias Settings
10
* @memberof sap
11
*/
12
13
14
SettingsExchange.prototype.connectionData
= null;
15
SettingsExchange.prototype.store
= null;
Kapsel Development
277
Kapsel Development
16
SettingsExchange.prototype.settingsSuccess
= null;
17
SettingsExchange.prototype.SettingsError
= null;
18
SettingsExchange.prototype.isInitialized
= false;
19
20
21
/**
22
event.
23
* @private
24
*/
25
26
27
28
sap.Settings.isInitialized = true;
29
var pd ="";
30
sap.Logon.unlock(function (connectionInfo) {
31
var userName =
connectionInfo["registrationContext"]["user"];
32
var password =
connectionInfo["registrationContext"]["password"];
33
var applicationConnectionId =
connectionInfo["applicationConnectionId"];
34
var securityConfig =
connectionInfo["registrationContext"]["securityConfig"];
35
var endpoint =
connectionInfo["applicationEndpointURL"];
36
37
endpoint.split("/");
var splitendpoint =
38
if (splitendpoint[0] == "https:")
39
40
278
keySSLEnabled="true";
41
42
if (securityConfig == null) {
43
securityConfig = "";
Kapsel Development
44
45
46
47
pd = appId+userName+password;
48
sap.Settings.store = new
sap.EncryptedStorage("SettingsStore", pd);
49
connectionData = {
50
"keyMAFLogonOperationContextConnectionData": {
51
"keyMAFLogonConnectionDataApplicationSettings":
52
53
"DeviceType":device.platform,
54
"DeviceModel":device.model,
55
"ApplicationConnectionId":applicationConnectionId
56
},
57
"keyMAFLogonConnectionDataBaseURL":burl
58
},
59
"keyMAFLogonOperationContextApplicationId":appId,
60
"keyMAFLogonOperationContextBackendUserName":userName,
61
"keyMAFLogonOperationContextBackendPassword":password,
62
"keyMAFLogonOperationContextSecurityConfig":securityConfig,
63
"keySSLEnabled":keySSLEnabled
64
};
65
sap.Settings.start(connectionData,
66
function(mesg) {
67
sap.Settings.isInitialized = true;
Kapsel Development
279
Kapsel Development
68
sap.Logger.debug("Setting Exchange is successful
"+mesg,"SMP_SETTINGS_JS",function(m){},function(m){});
69
},
70
function(mesg){
71
sap.Logger.debug("Setting Exchange failed" +
mesg,"SMP_SETTINGS_JS",function(m){},function(m){});
72
sap.Settings.isInitialized = false;
73
});
74
75
, function () {
76
sap.Logger.debug("unlock failed
","SMP_SETTINGS_JS",function(m){},function(m){});
77
78
);
79
80
81
};
82
83
document.addEventListener("onSapLogonSuccess",
doSettingExchange, false);
84
document.addEventListener("onSapResumeSuccess",
doSettingExchange, false);
85
86
SettingsExchange.prototype.reset = function(key, sucessCB,
errorCB)
87
88
if ((typeof(sap.Settings.store) != undefined) &&
(sap.Settings.store != null)) {
89
errorCB);
90
sap.Settings.store.removeItem(key, sucessCB,
} else {
91
92
93
280
}
}
Kapsel Development
94
95
96
/**
97
98
* @public
99
* @memberof sap.Settings
100
* @method start
101
* @param {String} connectionData The example below shows the
structure of the connection data.
102
* @param {function} successCallback Function to invoke if the
exchange is successful.
103
* @param {function} errorCallback Function to invoke if the
exchange failed.
104
* @example
105
* connectionData = {
106
"keyMAFLogonOperationContextConnectionData": {
107
"keyMAFLogonConnectionDataApplicationSettings":
108
109
"DeviceType":device.platform,
110
"DeviceModel":device.model,
111
"ApplicationConnectionId":"yourappconnectionid"
112
},
113
*
"keyMAFLogonConnectionDataBaseURL":"servername:port"
114
},
115
*
"keyMAFLogonOperationContextApplicationId":"yourapplicationid",
116
*
"keyMAFLogonOperationContextBackendUserName":"yourusername",
117
*
"keyMAFLogonOperationContextBackendPassword":"password",
118
*
"keyMAFLogonOperationContextSecurityConfig":"securityConfigName",
119
"keySSLEnabled":keySSLEnabled
120
};
Kapsel Development
281
Kapsel Development
121
* sap.Settings.start(connectionData, function(mesg) {
122
123
*
sap.Logger.debug("Setting Exchange is successful
"+mesg,"SMP_SETTINGS_JS",function(m){},function(m){});
124
125
},
function(mesg){
126
*
sap.Logger.debug("Setting
Exchange failed" + mesg,"SMP_SETTINGS_JS",function(m){},function(m)
{});
127
128
*/
});
129
SettingsExchange.prototype.start = function (connectionData,
successCallback, errorCallback) {
130
sap.Settings.settingsSuccess = successCallback;
131
sap.Settings.SettingsError = errorCallback;
132
sap.Settings.connectionData = connectionData;
133
sap.Logger.debug("Accessing the data from
vault","SMP_SETTINGS_JS",function(m){},function(m){});
134
sap.Settings.store.getItem("settingsdata",
sap.Settings.getStoreDataSuccess, sap.Settings.getStoreDataError);
135
136
137
};
138
139
140
141
142
143
144
/**
145
* This is a private function. End user will not use this
plugin directly.
146
* This function gets called after the start function is able
to read the current settings from the secured storage.
147
282
@private
Kapsel Development
148
* @param {String} value This is the value of the current
setting exchange stored in the secured store.
149
**/
150
151
SettingsExchange.prototype.getStoreDataSuccess
function(value){
152
storedSettings = value;
153
sap.Logger.debug("Exchanging the
data","SMP_SETTINGS_JS",function(m){},function(m){});
154
exec(sap.Settings.SettingsExchangeDone,
155
sap.Settings.SettingsExchangeError,
156
"SMPSettingsExchangePlugin",
157
"start",
[JSON.stringify(connectionData),storedSettings]);
158
159
160
/**
161
* This is a private function. End user will not use this
plugin directly.
162
* This function is called after the start function is unable
to read the current settings from the secured storage.
163
@private
164
* @param {String} message This is the error message produced
by the encrypted storage.
165
**/
166
SettingsExchange.prototype.getStoreDataError
function(mesage){
167
sap.Logger.debug("Setting exchange failed to read data
store: Proceeding without data",function(m){},function(m){});
168
169
170
171
/**
172
* This is a private function. End user will not use this
plugin directly.
173
* This function is called after the settings exchange
completes succefully.
Kapsel Development
283
Kapsel Development
174
@private
175
* @param {String} message This is the
the settings plugin completes successfully.
176
**/
177
178
SettingsExchange.prototype.SettingsExchangeDone =
function(message) {
179
sap.Logger.debug("Setting Exchange
Success","SMP_SETTINGS_JS",function(m){},function(m){});
180
var jsondata =
JSON.parse(message);
181
settingsString = JSON.stringify(jsondata["data"]);
182
sap.Settings.store.setItem("settingsdata",settingsString,sap.Settin
gs.SettingsWriteDone,sap.Settings.SettingsWriteError);
183
if (sap.Settings.settingsSuccess != null) {
184
sap.Logger.debug("Setting exchange
successful","SMP_SETTINGS_JS",function(m){},function(m){});
185
sap.Settings.settingsSuccess(jsondata["msg"]);
186
187
}
}
188
189
/**
190
* This is a private function. End user will not use this
plugin directly.
191
* This function is called after the settings exchange
completes succefully
192
@private
193
* @param {String} message This is the error message produced
when the settings plugin has an error.
194
**/
195
SettingsExchange.prototype.SettingsExchangeError =
function(message) {
196
sap.Logger.error("Setting Exchange failed calling the
error callback funciton","SMP_SETTINGS_JS",function(m){},function(m)
{});
197
198
284
if (sap.Settings.SettingsError != null) {
sap.Settings.SettingsError(message);
Kapsel Development
199
200
}
}
201
202
/**
203
* This is a private function. End user will not use this
plugin directly.
204
* This function is called after the setting data is stored
successfully.
205
@private
206
* @param {String} message This is the message produced upon
successful storing of settings to the encrypted store.
207
**/
208
SettingsExchange.prototype.SettingsWriteDone
function(message) {
209
sap.Logger.debug("Setting
stored","SMP_SETTINGS_JS",function(m){},function(m){});
210
211
212
213
/**
214
* This is a private function. End user will not use this
plugin directly.
215
* This function is called after the storing of the setting
data fails.
216
@private
217
* @param {String} message This is the message produced upon
failure to store the settings to the encrypted store.
218
**/
219
SettingsExchange.prototype.SettingsWriteError
function(message) {
220
sap.Logger.error("Setting store
failed","SMP_SETTINGS_JS",function(m){},function(m){});
221
222
223
/**
224
* This is a private function. End user will not use this
plugin directly.
Kapsel Development
285
Kapsel Development
225
* This function is called after the deviceready. This
uploads the logs to the server.
226
@private
227
* @param {boolean} uploadLog This indicates whether the
upload log is currently enabled or disbled.
228
**/
229
SettingsExchange.prototype.logLevelUpdated
function(logLevel)
230
231
sap.Logger.setLogLevel(logLevel,
sap.Settings.LogLevelSetSuccess, sap.Settings.LogLevelSetFailed);
232
sap.Logger.upload(sap.Settings.logUploadedSuccess,
sap.Settings.logUploadFailed);
233
234
235
/**
236
* This is a private function. End user will not use this
plugin directly.
237
238
@private
239
240
**/
241
SettingsExchange.prototype.LogLevelSetSuccess =
function(mesg){
242
sap.Logger.debug("Log level set
successful","SMP_SETTINGS_JS",function(m){},function(m){});
243
244
/**
245
* This is a private function. End user will not use this
plugin directly.
246
247
@private
248
249
**/
250
SettingsExchange.prototype.LogLevelSetFailed =
function(mesg){
286
Kapsel Development
251
sap.Logger.error("Log level set
failed","SMP_SETTINGS_JS",function(m){},function(m){});
252
253
254
/**
255
* This is a private function. End user will not use this
plugin directly.
256
257
@private
258
259
**/
260
SettingsExchange.prototype.logUploadedSuccess =
function(mesg){
261
sap.Logger.debug("Log upload
successful","SMP_SETTINGS_JS",function(m){},function(m){});
262
263
/**
264
* This is a private function. End user will not use this
plugin directly.
265
266
@private
267
268
**/
269
{
SettingsExchange.prototype.logUploadFailed = function(mesg)
270
sap.Logger.error("upload log
failed","SMP_SETTINGS_JS",function(m){},function(m){});
271
272
273
274
275
276
277
278
Kapsel Development
287
Kapsel Development
279
280
281
Value
ID
Name
Application name.
Vendor
Version
Type
Application type.
Native native iOS and Android applications.
Hybrid container-based applications, such as Kapsel.
Agentry meta data-driven applications, such as Agentry.
Application configuration options differ depending on your selection.
Description
9. Click Save.
Application-related tabs appear, and you are ready to configure the application, based on
the application type.
10. In the Backend page, enter the end point for the back end system to which the application
will connect. For example, http://localhost:8090/odata.
288
Kapsel Development
11. In the bottom right-hand corner, click Save.
12. Run the application on the device or emulator, and click Register.
13. In the registration page, enter the values, and click Register.
The user name and password combination should have permission to access the OData
backend.
14.
host the host is defined by host name and should match with the domain registered with
SAP Mobile Platform. If the requested domain name does not match, default domain is
used..
port the port for listening to OData-based requests. By default the port number is 8080.
public if included, an anonymous connection is allowed.
odata/applications/ refers to the OData services associated with the application
resources.
{latest|v1} version of the service document.
appid name of the application.
Connections name of the OData collection.
Application connection properties are optional. You can create an application connection
without including any application properties.
DeviceType is an application connection property that you may set. Valid values for
DeviceType are:
Kapsel Development
289
Kapsel Development
Response
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://
schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xml:base="https://<smp base URL>/odata/applications/latest/
e2eTest/">
<id>https://<application URL>/odata/applications/latest/e2eTest/
Connections('4891dd0f-0735-47cc-a599-76bf8a16d457')</id>
<title type="text" />
<updated>2012-10-19T09:05:25Z</updated>
<author>
<name />
</author>
<link rel="edit" title="Connection"
href="Connections('4891dd0f-0735-47cc-a599-76bf8a16d457')" />
<category term="applications.Connection" scheme="http://
schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:ETag>2012-10-19 14:35:24.0</d:ETag>
<d:ApplicationConnectionId>4891dd0f-0735-47cca599-76bf8a16d457</d:ApplicationConnectionId>
<d:AndroidGcmPushEnabled m:type="Edm.Boolean">false</
d:AndroidGcmPushEnabled>
<d:AndroidGcmRegistrationId>398123745023</d
AndroidGcmRegistrationId>
290
Kapsel Development
<d:AndroidGcmSenderId />
<d:ApnsPushEnable m:type="Edm.Boolean">false</d:ApnsPushEnable>
<d:ApnsDeviceToken />
<d:ApplicationVersion>1.0</d:ApplicationVersion>
<d:BlackberryPushEnabled m:type="Edm.Boolean">false</
d:BlackberryPushEnabled>
<d:BlackberryDevicePin m:null="true" />
<d:BlackberryBESListenerPort m:type="Edm.Int32">0</
d:BlackberryBESListenerPort>
<d:BlackberryPushAppID m:null="true" />
<d:BlackberryPushBaseURL m:null="true" />
<d:BlackberryPushListenerPort m:type="Edm.Int32">0</
d:BlackberryPushListenerPort>
<d:BlackberryListenerType m:type="Edm.Int32">0</
d:BlackberryListenerType>
<d:CustomizationBundleId />
<d:CustomCustom1 />
<d:CustomCustom2 />
<d:CustomCustom3 />
<d:CustomCustom4 />
<d:DeviceModel m:null="true" />
<d:DeviceType>Unknown</d:DeviceType>
<d:DeviceSubType m:null="true" />
<d:DevicePhoneNumber m:null="true" />
<d:DeviceIMSI m:null="true" />
<d:PasswordPolicyEnabled m:type="Edm.Boolean">false</
d:PasswordPolicyEnabled>
<d:PasswordPolicyDefaultPasswordAllowed
m:type="Edm.Boolean">false</d:PasswordPolicyDefaultPasswordAllowed>
<d:PasswordPolicyMinLength m:type="Edm.Int32">0</
d:PasswordPolicyMinLength>
<d:PasswordPolicyDigitRequired m:type="Edm.Boolean">false</
d:PasswordPolicyDigitRequired>
<d:PasswordPolicyUpperRequired m:type="Edm.Boolean">false</
d:PasswordPolicyUpperRequired>
<d:PasswordPolicyLowerRequired m:type="Edm.Boolean">false</
d:PasswordPolicyLowerRequired>
<d:PasswordPolicySpecialRequired m:type="Edm.Boolean">false</
d:PasswordPolicySpecialRequired>
<d:PasswordPolicyExpiresInNDays m:type="Edm.Int32">0</
d:PasswordPolicyExpiresInNDays>
<d:PasswordPolicyMinUniqueChars m:type="Edm.Int32">0</
d:PasswordPolicyMinUniqueChars>
<d:PasswordPolicyLockTimeout m:type="Edm.Int32">0</
d:PasswordPolicyLockTimeout>
<d:PasswordPolicyRetryLimit m:type="Edm.Int32">0</
d:PasswordPolicyRetryLimit>
<d:ProxyApplicationEndpoint>http://<backend URL></
d:ProxyApplicationEndpoint>
<d:ProxyPushEndpoint>http[s]://<host:port>/Push</
d:ProxyPushEndpoint>
<d:MpnsChannelURI m:null="true" />
<d:WnsChannelURI m:null="true" />
</m:properties>
</content>
</entry>
Kapsel Development
291
Kapsel Development
CORS Support
Cross-domain HTTP requests are requests for resources from a different domain than the
domain of the resource making the request. Cross-Origin Resource Sharing (CORS)
mechanism provides a way for web servers to support cross-site access controls, which enable
secure cross-site data transfers.
Response
<?xml version='1.0' encoding='utf-8'?>
<entry xmlns="http://www.w3.org/2005/Atom"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/
dataservices/metadata"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/
dataservices"
xml:base="https://<smp base URL>/odata/applications/v1/
e2eTest/">
<id>http://https://mobilesmpdev.netweaver.ondemand.com/smp/odata/
applications/v1/e2eTest/
Connections('c9d8a9da-9f36-4ae5-9da5-37d6d90483b5')</id>
<title type="text" />
<updated>2012-06-28T09:55:48Z</updated>
<author><name /></author>
<link rel="edit" title="Connections"
href="Connections('c9d8a9da-9f36-4ae5-9da5-37d6d90483b5')" />
<category term="applications.Connection" scheme="http://
schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:ETag m:type="Edm.DateTime">2012-06-28T17:55:47.685</d:ETag>
<d:ApplicationConnectionId>c9d8a9da-9f36-4ae5-9da5-37d6d90483b5</
d:ApplicationConnectionId>
<d:AndroidGcmPushEnabled m:type="Edm.Boolean">false</
d:AndroidGcmPushEnabled>
<d:AndroidGcmRegistrationId m:null="true" />
292
Kapsel Development
<d:AndroidGcmSenderId m:null="true" />
<d:ApnsPushEnable m:type="Edm.Boolean">true</d:ApnsPushEnable>
<d:ApnsDeviceToken m:null="true" />
<d:ApplicationVersion m:null="true" />
<d:BlackberryPushEnabled m:type="Edm.Boolean">true</
d:BlackberryPushEnabled>
<d:BlackberryDevicePin>00000000</d:BlackberryDevicePin>
<d:BlackberryBESListenerPort m:type="Edm.Int32">5011</
d:BlackberryBESListenerPort>
<d:BlackberryPushAppID m:null="true" />
<d:BlackberryPushBaseURL m:null="true" />
<d:BlackberryPushListenerPort m:type="Edm.Int32">0</
d:BlackberryPushListenerPort>
<d:BlackberryListenerType m:type="Edm.Int32">0</
d:BlackberryListenerType>
<d:CustomCustom1>custom1</d:CustomCustom1>
<d:CustomCustom2 m:null="true" />
<d:CustomCustom3 m:null="true" />
<d:CustomCustom4 m:null="true" />
<d:DeviceModel m:null="true" />
<d:DeviceType>Unknown</d:DeviceType>
<d:DeviceSubType m:null="true" />
<d:DevicePhoneNumber>12345678901</d:DevicePhoneNumber>
<d:DeviceImsi m:null="true" />
<d:PasswordPolicyEnabled m:type="Edm.Boolean">true</
d:PasswordPolicyEnabled>
<d:PasswordPolicyDefaultPasswordAllowed
m:type="Edm.Boolean">false</d:PasswordPolicyDefaultPasswordAllowed>
<d:PasswordPolicyMinLength m:type="Edm.Int32">8</
d:PasswordPolicyMinLength>
<d:PasswordPolicyDigitRequired m:type="Edm.Boolean">false</
d:PasswordPolicyDigitRequired>
<d:PasswordPolicyUpperRequired m:type="Edm.Boolean">false</
d:PasswordPolicyUpperRequired>
<d:PasswordPolicyLowerRequired m:type="Edm.Boolean">false</
d:PasswordPolicyLowerRequired>
<d:PasswordPolicySpecialRequired m:type="Edm.Boolean">false</
d:PasswordPolicySpecialRequired>
<d:PasswordPolicyExpiresInNDays m:type="Edm.Int32">0</
d:PasswordPolicyExpiresInNDays>
<d:PasswordPolicyMinUniqueChars m:type="Edm.Int32">0</
d:PasswordPolicyMinUniqueChars>
<d:PasswordPolicyLockTimeout m:type="Edm.Int32">0</
d:PasswordPolicyLockTimeout>
<d:PasswordPolicyRetryLimit m:type="Edm.Int32">20</
d:PasswordPolicyRetryLimit>
<d:ProxyApplicationEndpointm:null="true" />
<d:ProxyPushEndpoint>http://xxue-desktop:8080/GWC/
SMPNotification</d:ProxyPushEndpoint>
<d:WnsChannelURI m:null="true" />
<d:MpnsChannelURI m:null="true" />
<d:WnsPushEnable m:type="Edm.Boolean">false</d:WnsPushEnable>
<d:MpnsPushEnable m:type="Edm.Boolean">true</d:MpnsPushEnable>
</m:properties>
</content>
</entry>
Kapsel Development
293
Kapsel Development
You can also retrieve a property value by appending the property name in the URL. For
example, to retrieve the ClientLogLevel property value, enter:
http[s]://<host:port>/[public/]odata/applications/{v1|latest}/
{appid}/Connections({appcid})/ClientLogLevel
Client-side Debugging
Debug the Kapsel application on the device or by using a desktop browser.
Debugging in a Desktop Browser
Debug the JavaScript code running in a deskstop browser.
This procedure shows how to debug using Chrome. See https://developers.google.com/
chrome-developer-tools/. In some cases, debugging on the device is necessary, for example,
when you debug touch, or code that includes JavaScript files from Apache Cordova or Kapsel,
since these expect to run on a mobile device or simulator.
1. In the Chrome menu, choose Tools > Developer Tools.
2. Click Sources to open a source file.
3. Set break points to step through the code.
4. Use the Network tab to examine the OData URL sent and the values received.
Debugging on iOS
This procedure demonstrates how to debug an app that includes Apache Cordova and Kapsel
plugins.
This procedure requires a device or simulator running iOS 6 and a Mac that has Safari 6.
1. Connect the device to the Mac with a USB cable, or start the simulator.
2. On the device or simulator, go to Settings > Safari > Advanced > Web Inspector, and
turn it to On.
3. On the device or simulator, open your Kapsel app or a Web page in the Safari browser.
4. On the Mac, in Safari, choose Develop > iPhone Simulator > index.html.
294
Kapsel Development
Generating and Uploading Kapsel App Files Using the Command Line
Interface
The Kapsel command line interface provides a way to generate a ZIP file that contains the
HTML files that make up the app.
Kapsel Development
295
Kapsel Development
1. Open a command prompt window, or terminal, and navigate to the folder that contains the
Kapsel command line interface, for example:
On Windows:
SDK_HOME\MobileSDK3\KapselSDK\cli
On Mac:
~SDK_HOME/MobileSDK3/KapselSDK/cli
2. Run the command:
npm -g install
On Mac, you may need to run the command as sudo:
sudo npm -g install
3. Change directories to the directory containing the project and run the command:
kapsel package
kapsel deploy <com.mycompany.app_ID> localhost:<port>
<Admin_user_name> <Admin_password>
You can, optionally, enter a platform in the package command, such as android or ios. The
parameters to the deploy command are the app ID, the SAP Mobile Platform Server host
name, and the user ID and password for Management Cockpit.
The ZIP file containing the HTML files that make up the app is generated and then
uploaded to SAP Mobile Platform Server, and the Management Cockpit shows that
revision x was uploaded.
Changing the Default Port
By default, the Kapsel command line interface is configured to use port 8083, as this is the
default used for Management Cockpit when installing SAP Mobile Platform Server.
If port 8083 is in use during installation, the installer automatically assigns a different port and
notifies you. If the port number changes from 8083, you can change it using the command
line.
1. Open a command prompt window, or terminal.
2. Specify the server paramater in the format server:port, for example:
kapsel deploy <com.mycompany.app_ID> localhost:<port>
<Admin_user_name> <Admin_password>
This example shows how to deploy the Kapsel application called "com.sample.app" using
port 8084.
kapsel deploy com.sample.app localhost:8084 smpAdmin
s3pAdmin
296
Kapsel Development
1. In the Android IDE or Xcode, right-click the project's www folder, and compress the items
to package the files in a ZIP file.
2. Log in to the Management Cockpit to upload the app.
Contains the contents of the application's www folder and config.xml of the project,
with a separate folder in the archive for each mobile platform (android/www and/or
ios/www in all lower case). Format structure for hybrid apps:
|- android
| |- config.xml
| |- www
|- iOS
...
Task
1. From Management Cockpit, select Applications > App Specific Settings.
2. Under Upload, click Browse.
a) In the dialog, navigate to the directory.
b) Select the hybrid app package, and confirm.
New version information appears for the uploaded Kapsel app for each mobile platform.
You cannot change this information.
Property
Description
Identifies the Kapsel SDK version used to develop the Kapsel app, for example 3.0.0.
Note: This version attribute is informational
only, and is not used by SAP Mobile Platform
Server to determine whether device clients
should receive the Web application update.
Development Version
Description
Kapsel Development
297
Kapsel Development
Property
Description
Revision
298
Kapsel Development
Syntax
Perform a POST request to the following URI:
https://<host>:<admin_port>/Admin/kapsel/jaxrs/KapselApp/{APP_ID}
Parameters
file The file that contains the application archive, sent as multipart/form-data.
Returns
A response providing information about the new and current version of the application. For
example:
{"newVersion":
{"requiredKapselVersion":"1.5",
"developmentVersion":"1.2.5",
"description":"An update for the sample app.",
"revision":-1},
"currentVersion":
{"requiredKapselVersion":"1.5",
"developmentVersion":"1.2.4",
"description":"A sample app.",
"revision":2}
}
On successful deployment, the client receives a 201 status code; otherwise, an HTTP failure
code and message.
Examples
Note: This example users the curl command line client and the --cacert flag. Your client
may require you to pass other arguments or set specific configuration options.
Kapsel Development
299
Kapsel Development
plugins, SAP recommends that you start from a clean state and add only the plugins you
require.
1. Open a command prompt window and navigate to the Cordova project's directory.
2. (Optional) To see a list of installed plugins, enter:
cordova plugins
3. Enter:
cordova plugin remove <plugin_name>
For example, to remove the Logger plugin, enter:
cordova plugin remove com.sap.mp.cordova.plugins.logger
300
Index
Index
A
addEventListener
method 25
Android SDK 3
Apache Cordova 1
Apache Cordova Command Line Interface
installing 3
APNs 212
App ID 213
applicationId
member 49
applications 297
configuring applications 7
creating an application definition 7
AppUpdate 17
namespace 23
AppUpdate plugin
adding 22
overview 17
appupdate.js
source file 31
authentication
application, defining 11
AuthProxy
namespace 126
AuthProxy plugin
adding 122
client certificate 125
keytool utility 125
overview 122
register with SAP Mobile Platform Server
125
sap.AuthProxy.CertificateFromLogonManage
r("clientKey") 125
authproxy.js
source file 145
C
callback
type 230
CertificateFromFile
class 129
CertificateFromLogonManager
class 131
Kapsel Development
CertificateFromStore
class 132
changePassword
method 51
checkForNotification
method 225
checking
event 27
class
CertificateFromFile 129
CertificateFromLogonManager 131
CertificateFromStore 132
clear
method 252
Client Hub 46
client logs
examples 182
viewing 182
clienthubEntitlements keychain group 46
COMPLEX_STRING_MAXIMUM_LENGTH
member 250
config.xml 13
configuring applications 7
Cordova config.xml file 15
core
member 50
creating an application definition 7
creating application endpoint URL 9
creating back-end connection 9
D
debug
method 188
debugging 294
in a desktop browser 294
default port
changing 296
defining
application authentication 11
deleteCertificateFromStore
method 137
deleteCertificateSuccessCallback
type 143
deploying
hybrid app REST API 298
301
Index
downloading
event 28
E
EncryptedStorage 246
namespace 248
EncryptedStorage plugin
adding 247
encryptedstorage.js
source file 260
ERR_CERTIFICATE_ALIAS_NOT_FOUND
member 132
ERR_CERTIFICATE_FILE_NOT_EXIST
member 133
ERR_CERTIFICATE_INVALID_FILE_FORMAT
member 133
ERR_CLIENT_CERTIFICATE_VALIDATION
member 133
ERR_FILE_CERTIFICATE_SOURCE_UNSUPP
ORTED
member 133
ERR_GET_CERTIFICATE_FAILED
member 134
ERR_HTTP_TIMEOUT
member 134
ERR_INVALID_PARAMETER_VALUE
member 134
ERR_LOGON_MANAGER_CERTIFICATE_ME
THOD_NOT_AVAILABLE
member 134
ERR_LOGON_MANAGER_CORE_NOT_AVAIL
ABLE
member 135
ERR_MISSING_PARAMETER
member 135
ERR_NO_SUCH_ACTION
member 135
ERR_SERVER_CERTIFICATE_VALIDATION
member 136
ERR_SERVER_REQUEST_FAILED
member 136
ERR_SYSTEM_CERTIFICATE_SOURCE_UNS
UPPORTED
member 136
ERR_UNKNOWN
member 136
error
event 28
method 189
302
ERROR_BAD_PASSWORD
member 250
ERROR_GREATER_THAN_MAXIMUM_SIZE
member 251
ERROR_INVALID_PARAMETER
member 251
ERROR_UNKNOWN
member 251
errorCallback
type 59, 143, 257
event
checking 27
downloading 28
error 28
noupdate 29
updateready 29
F
files
source 30, 65, 145, 197, 230, 259, 277
G
GCM 214
generateODataHttpClient
method 138
get
method 51, 139
getBadgeNumber
method 226
getItem
method 252
getItemSuccessCallback
type 258
getLogLevel
method 191
getSuccessCallback
type 60
Git
installing 3
Google Cloud Messaging Service 214
H
hybrid apps
deploying, REST API reference 298
Index
I
info
method 192
init
method 52
Logon plugin
configuring default values 45
installing 43
Logon.core.deleteRegistration() 46
LogonController.js
source file 66
Java SDK
adding to PATH 3
installing 3
managePasscode
method 56
member
applicationId 49
COMPLEX_STRING_MAXIMUM_LENGT
H 250
core 50
ERR_CERTIFICATE_ALIAS_NOT_FOUN
D 132
ERR_CERTIFICATE_FILE_NOT_EXIST
133
ERR_CERTIFICATE_INVALID_FILE_FOR
MAT 133
ERR_CLIENT_CERTIFICATE_VALIDATIO
N 133
ERR_FILE_CERTIFICATE_SOURCE_UNS
UPPORTED 133
ERR_GET_CERTIFICATE_FAILED 134
ERR_HTTP_TIMEOUT 134
ERR_INVALID_PARAMETER_VALUE 134
ERR_LOGON_MANAGER_CERTIFICATE
_METHOD_NOT_AVAILABLE
134
ERR_LOGON_MANAGER_CORE_NOT_A
VAILABLE 135
ERR_MISSING_PARAMETER 135
ERR_NO_SUCH_ACTION 135
ERR_SERVER_CERTIFICATE_VALIDATI
ON 136
ERR_SERVER_REQUEST_FAILED 136
ERR_SYSTEM_CERTIFICATE_SOURCE_
UNSUPPORTED 136
ERR_UNKNOWN 136
ERROR_BAD_PASSWORD 250
ERROR_GREATER_THAN_MAXIMUM_S
IZE 251
ERROR_INVALID_PARAMETER 251
ERROR_UNKNOWN 251
Logger#DEBUG 187
Logger#ERROR 187
Logger#INFO 188
K
Kapsel apps
similar to hybrid apps 297
uploading new version 297
Kapsel command line interface
generating files 295
key
method 253
keySuccessCallback
type 258
L
length
method 254
lengthSuccessCallback
type 259
life cycle
Kapsel apps 297
lock
method 56
Logger
namespace 184
Logger plugin 178
logger.js
source file 197
Logger#DEBUG
member 187
Logger#ERROR
member 187
Logger#INFO
member 188
Logger#WARN
member 188
Logon
namespace 47
Kapsel Development
303
Index
Logger#WARN 188
SIMPLE_STRING_MAXIMUM_LENGTH
251
method
addEventListener 25
changePassword 51
checkForNotification 225
clear 252
debug 188
deleteCertificateFromStore 137
error 189
generateODataHttpClient 138
get 51, 139
getBadgeNumber 226
getItem 252
getLogLevel 191
info 192
init 52
key 253
length 254
lock 56
managePasscode 56
registerForNotificationTypes 227
reloadApp 26
removeEventListener 26
removeItem 255
reset 27
resetBadge 228
sendRequest 141
set 57
setBadgeNumber 229
setItem 256
setLogLevel 193
showRegistrationData 58
start 276
unlock 59
unregisterForNotificationTypes 229
update 27
upload 195
warn 196
N
namespace
AppUpdate 23
AuthProxy 126
EncryptedStorage 248
Logger 184
Logon 47
Push 224
304
Settings 276
Node.js
installing 3
noupdate
event 29
P
Plugman 215
provisioning file 214
proxy server
configuring for Git 3
configuring for npm 3
Push
namespace 224
push.js
source file 231
R
registerForNotificationTypes
method 227
reloadApp
method 26
removeEventListener
method 26
removeItem
method 255
reset
method 27
resetBadge
method 228
REST API reference
hybrid app, deploying 298
S
sap.Logon.init() 46
sendRequest
method 141
set
method 57
setBadgeNumber
method 229
setItem
method 256
setLogLevel
method 193
Index
Settings
namespace 276
Settings plugin 274
adding 275
overview 274
settings.js
source file 277
showRegistrationData
method 58
SIMPLE_STRING_MAXIMUM_LENGTH
member 251
source
files 30, 65, 145, 197, 230, 259, 277
source file
appupdate.js 31
authproxy.js 145
encryptedstorage.js 260
logger.js 197
LogonController.js 66
push.js 231
settings.js 277
start
method 276
successCallback
type 60, 144, 259
successCallbackNoParameters
type 65
deleteCertificateSuccessCallback 143
errorCallback 59, 143, 257
getItemSuccessCallback 258
getSuccessCallback 60
keySuccessCallback 258
lengthSuccessCallback 259
successCallback 60, 144, 259
successCallbackNoParameters 65
U
unlock
method 59
unregisterForNotificationTypes
method 229
update
method 27
updateready
event 29
upload
method 195
uploading
Kapsel apps 297
W
warn
method 196
type
callback 230
Kapsel Development
305
Index
306