Skip to content

Commit

Permalink
Merge e9a97d4 into d8a67db
Browse files Browse the repository at this point in the history
  • Loading branch information
Mykola Mokhnach committed Jul 2, 2018
2 parents d8a67db + e9a97d4 commit 3ac119d
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 16 deletions.
101 changes: 94 additions & 7 deletions lib/android-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from 'path';
import { exec } from 'teen_process';
import { retry, retryInterval } from 'asyncbox';
import logger from './logger';
import { fs } from 'appium-support';
import { fs, tempDir } from 'appium-support';
import { path as unicodeIMEPath } from 'appium-android-ime';
import { path as settingsApkPath } from 'io.appium.settings';
import { path as unlockApkPath } from 'appium-unlock';
Expand Down Expand Up @@ -96,23 +96,110 @@ helpers.getJavaVersion = async function () {
return javaVer;
};

/**
* Installs the given certificate on a rooted real device or
* an emulator. The emulator must be executed with `-writable-system`
* command line option and adb daemon should be running in root
* mode for this method to work properly. The method also requires
* openssl tool to be available on the destination system.
* Read https://github.com/appium/appium/issues/10964
* for more details on this topic
*
* @param {ADB} adb - The ADB helper instance
* @param {Buffer|string} cert - base64-decoded content of the actual certificate
* represented as a string or a buffer
*/
helpers.installMitmCertificate = async function (adb, cert) {
try {
await fs.which('openssl');
} catch (err) {
throw new Error('To install a custom cert, the openssl tool must be installed ' +
'on the system and available on the path');
}

if (!_.isBuffer(cert)) {
cert = Buffer.from(cert, 'base64');
}

const tempCert = await tempDir.open({
prefix: 'cert',
suffix: '.cer'
});
try {
await fs.writeFile(tempCert.path, cert);
let {stdout} = await exec('openssl', ['x509', '-noout', '-hash', '-in', tempCert.path]);
const certHash = stdout.trim();
logger.debug(`Got certificate hash: ${certHash}`);
logger.debug('Preparing certificate content');
({stdout} = await exec('openssl', ['x509', '-in', tempCert.path], {isBuffer: true}));
let dstCertContent = stdout;
({stdout} = await exec('openssl', ['x509', '-in', tempCert.path, '-text', '-fingerprint', '-noout'], {isBuffer: true}));
dstCertContent = dstCertContent.concat(stdout);
const dstCert = await tempDir.open({
prefix: certHash,
suffix: '.0'
});
try {
await fs.writeFile(dstCert.path, dstCertContent);
logger.debug('Mounting /system endpoint');
await adb.shell(['mount', '-o', 'rw,remount', '/system']);
const dstPath = '/system/etc/security/cacerts';
logger.debug(`Uploading the generated certificate from '${dstCert.path}' to '${dstPath}'`);
await adb.push(dstCert.path, dstPath);
logger.debug('Remounting the remote file system');
await adb.adbExec(['remount']);
} finally {
await fs.rimraf(dstCert.path);
}
} catch (err) {
throw new Error(`Cannot inject the custom certificate. Do you have root permissions on the device? ` +
`Original error: ${err.message}`);
} finally {
await fs.rimraf(tempCert.path);
}
};

helpers.prepareEmulator = async function (adb, opts) {
let {avd, avdArgs, language, locale, avdLaunchTimeout,
avdReadyTimeout} = opts;
let {
avd,
avdArgs,
language,
locale,
avdLaunchTimeout,
avdReadyTimeout,
mitmCertificate,
} = opts;
if (!avd) {
throw new Error("Cannot launch AVD without AVD name");
}
let avdName = avd.replace('@', '');
let runningAVD = await adb.getRunningAVD(avdName);
if (runningAVD !== null) {
if (avdArgs && avdArgs.toLowerCase().indexOf("-wipe-data") > -1) {
logger.debug(`Killing '${avdName}' because it needs to be wiped at start.`);
await adb.killEmulator(avdName);
} else {
if (!_.includes(avdArgs, '-wipe-data') && !mitmCertificate) {
logger.debug("Not launching AVD because it is already running.");
return;
}
logger.debug(`Killing '${avdName}'`);
await adb.killEmulator(avdName);
}

if (mitmCertificate) {
logger.debug('Installing the provided MITM Certificate');
await adb.launchAVD(avd, '-writable-system', null, null, avdLaunchTimeout, avdReadyTimeout);

const isRoot = await adb.root();
try {
await this.installMitmCertificate(adb, mitmCertificate);
} finally {
if (isRoot) {
await adb.unroot();
}
}

logger.info(`Restarting the emulator '${avd}' in order to finalize certificate deployment`);
await adb.killEmulator(avdName);
}

avdArgs = this.prepareAVDArgs(opts, adb, avdArgs);
await adb.launchAVD(avd, avdArgs, language, locale, avdLaunchTimeout,
avdReadyTimeout);
Expand Down
3 changes: 3 additions & 0 deletions lib/desired-caps.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ let uiautomatorCapConstraints = {
userProfile: {
isNumber: true
},
mitmCertificate: {
isString: true
},
};

let desiredCapConstraints = {};
Expand Down
24 changes: 15 additions & 9 deletions lib/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,17 @@ class AndroidDriver extends BaseDriver {
try {
let [sessionId, caps] = await super.createSession(...args);

let serverDetails = {platform: 'LINUX',
webStorageEnabled: false,
takesScreenshot: true,
javascriptEnabled: true,
databaseEnabled: false,
networkConnectionEnabled: true,
locationContextEnabled: false,
warnings: {},
desired: this.caps};
let serverDetails = {
platform: 'LINUX',
webStorageEnabled: false,
takesScreenshot: true,
javascriptEnabled: true,
databaseEnabled: false,
networkConnectionEnabled: true,
locationContextEnabled: false,
warnings: {},
desired: this.caps
};

this.caps = Object.assign(serverDetails, this.caps);

Expand Down Expand Up @@ -174,6 +176,10 @@ class AndroidDriver extends BaseDriver {
}
}

if (this.opts.mitmCertificate && !this.isEmulator()) {
await helpers.installMitmCertificate(this.adb, this.opts.mitmCertificate);
}

await this.startAndroidSession(this.opts);
return [sessionId, this.caps];
} catch (e) {
Expand Down

0 comments on commit 3ac119d

Please sign in to comment.