Skip to content

Commit c5fd5ad

Browse files
committed
add security profiles/credentials command
1 parent 543f9a8 commit c5fd5ad

4 files changed

Lines changed: 303 additions & 2 deletions

File tree

lib/cli.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ Cli.printUsage = function printUsage(d) {
314314

315315
w('\n');
316316

317-
var rightColumn = 43;
317+
var rightColumn = 45;
318318
var dots = '';
319319
var indent = '';
320320
var x, arg;

lib/ionic/package.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ var fs = require('fs'),
44
_ = require('underscore'),
55
Q = require('q'),
66
moment = require('moment'),
7-
request = require('request'),
87
ProgressBar = require('progress'),
98
IonicAppLib = require('ionic-app-lib'),
109
Utils = IonicAppLib.utils,

lib/ionic/security.js

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
var fs = require('fs'),
2+
path = require('path'),
3+
Q = require('q'),
4+
expandTilde = require('expand-tilde'),
5+
_ = require('underscore'),
6+
moment = require('moment'),
7+
IonicAppLib = require('ionic-app-lib'),
8+
Utils = IonicAppLib.utils,
9+
Login = IonicAppLib.login,
10+
Security = IonicAppLib.security,
11+
LoginTask = require('./login'),
12+
Prompt = require('./prompt'),
13+
Table = require('./table'),
14+
Task = require('./task').Task;
15+
16+
var Project = IonicAppLib.project;
17+
var IonicTask = function() {};
18+
19+
IonicTask.prototype = new Task();
20+
21+
IonicTask.prototype.run = function(ionic, argv) {
22+
23+
var cmd,
24+
subcmd;
25+
26+
if (argv._.length < 2) {
27+
cmd = 'profiles';
28+
} else {
29+
cmd = argv._[1];
30+
}
31+
32+
var dir = null,
33+
project = null,
34+
appId = null;
35+
36+
try {
37+
dir = process.cwd();
38+
project = Project.load(dir);
39+
appId = project.get('app_id');
40+
41+
if (!appId) {
42+
throw new Error('Missing Ionic App ID.');
43+
}
44+
} catch (ex) {
45+
return Utils.fail(ex, 'security');
46+
}
47+
48+
switch (cmd) {
49+
case 'profiles':
50+
if (argv._.length < 3) {
51+
subcmd = 'list';
52+
} else {
53+
subcmd = argv._[2];
54+
}
55+
56+
switch (subcmd) {
57+
case 'add':
58+
return addSecurityProfile(ionic, argv, dir, appId);
59+
case 'list':
60+
return listSecurityProfiles(ionic, argv, dir, appId);
61+
default:
62+
return Utils.fail("Unknown subcommand 'profiles " + subcmd + "'.", 'security')
63+
}
64+
case 'credentials':
65+
return addSecurityCredentials(ionic, argv, dir, appId);
66+
}
67+
68+
return Utils.fail("Unknown subcommand '" + cmd + "'.", 'security')
69+
70+
};
71+
72+
function addSecurityProfile(ionic, argv, dir, appId) {
73+
74+
if (argv._.length < 4) {
75+
return Utils.fail("Specify a name for this Security Profile. Example: 'ionic security profile add \"My New Profile\"'.", 'security');
76+
}
77+
78+
var jar,
79+
name = argv._[3];
80+
81+
return Login.retrieveLogin()
82+
.then(function(jar) {
83+
if (!jar) {
84+
console.log('No previous login existed. Attempting to log in now.');
85+
return LoginTask.login(argv);
86+
}
87+
return jar;
88+
})
89+
.then(function(j) {
90+
jar = j;
91+
92+
return Security.addProfile(appId, jar, name);
93+
})
94+
.then(function(body) {
95+
console.success('Added "' + name + '".');
96+
}, function(err) {
97+
if (typeof err === 'object') {
98+
if (err.type == 'ProfileExists') {
99+
console.error(err.title);
100+
} else {
101+
return Q.reject(new Error(err.title));
102+
}
103+
}
104+
})
105+
.catch(function(ex) {
106+
Utils.fail(ex, 'package');
107+
});
108+
109+
}
110+
111+
function listSecurityProfiles(ionic, argv, dir, appId) {
112+
113+
var jar;
114+
115+
return Login.retrieveLogin()
116+
.then(function(jar) {
117+
if (!jar) {
118+
console.log('No previous login existed. Attempting to log in now.');
119+
return LoginTask.login(argv);
120+
}
121+
return jar;
122+
})
123+
.then(function(j) {
124+
jar = j;
125+
126+
return Security.listProfiles(appId, jar);
127+
})
128+
.then(function(body) {
129+
if (body.data.length === 0) {
130+
console.log('You don\'t have any Security Profiles yet!');
131+
console.log('Type ' + 'ionic help security'.yellow + ' to learn how to use Security Profiles.');
132+
} else {
133+
var table = new Table({ head: ['name', 'tag', 'android', 'ios'] });
134+
135+
_.each(body.data, function(profile) {
136+
table.push([
137+
profile.name,
138+
profile.tag,
139+
typeof profile.credentials.android === 'undefined' ? '✗'.red.bold : '✓'.green.bold,
140+
typeof profile.credentials.ios === 'undefined' ? '✗'.red.bold : '✓'.green.bold
141+
]);
142+
});
143+
144+
console.log('');
145+
console.log(table.toString());
146+
console.log('');
147+
}
148+
})
149+
.catch(function(ex) {
150+
Utils.fail(ex, 'security');
151+
});
152+
}
153+
154+
function addSecurityCredentials(ionic, argv, dir, appId) {
155+
156+
if (argv._.length < 3) {
157+
return Utils.fail("Specify a valid platform (android or ios).", 'security');
158+
}
159+
160+
if (typeof argv.profile === 'undefined') {
161+
return Utils.fail("Specify the Security Profile on which these credentials are saved (--profile <tag>).", 'security');
162+
}
163+
164+
var jar,
165+
platform = argv._[2],
166+
profile = argv.profile;
167+
168+
if (!_.contains(['android', 'ios'], platform)) {
169+
return Utils.fail("Invalid platform '" + platform + "', please choose either 'android' or 'ios'.", 'security');
170+
}
171+
172+
return Login.retrieveLogin()
173+
.then(function(jar) {
174+
if (!jar) {
175+
console.log('No previous login existed. Attempting to log in now.');
176+
return LoginTask.login(argv);
177+
}
178+
return jar;
179+
})
180+
.then(function(j) {
181+
var q = Q.defer();
182+
183+
jar = j;
184+
185+
if (platform === 'android') {
186+
if (typeof argv.s !== 'undefined') {
187+
argv['keystore'] = argv.s;
188+
}
189+
190+
if (typeof argv.p !== 'undefined') {
191+
argv['keystore-password'] = argv.p;
192+
}
193+
194+
if (typeof argv.k !== 'undefined') {
195+
argv['key-alias'] = argv.k;
196+
}
197+
198+
if (typeof argv.w !== 'undefined') {
199+
argv['key-password'] = argv.w;
200+
}
201+
202+
Prompt.prompt([
203+
{ name: 'keystore', description: 'Keystore File:'.prompt, required: true },
204+
{ name: 'keystore-password', description: 'Keystore Password:'.prompt, hidden: true, required: true },
205+
{ name: 'key-alias', description: 'Key Alias:'.prompt, required: true },
206+
{ name: 'key-password', description: 'Key Password:'.prompt, hidden: true, required: true }
207+
], argv)
208+
.then(function(result) {
209+
q.resolve(result);
210+
})
211+
.catch(function(ex) {
212+
q.reject(ex);
213+
});
214+
} else if (platform === 'ios') {
215+
if (typeof argv.c !== 'undefined') {
216+
argv['cert'] = argv.c;
217+
}
218+
219+
if (typeof argv.p !== 'undefined') {
220+
argv['cert-password'] = argv.p;
221+
}
222+
223+
if (typeof argv.r !== 'undefined') {
224+
argv['provisioning-profile'] = argv.r;
225+
}
226+
227+
Prompt.prompt([
228+
{ name: 'cert', description: 'Certificate File:'.prompt, required: true },
229+
{ name: 'cert-password', description: 'Certificate Password:'.prompt, hidden: true, required: true },
230+
{ name: 'provisioning-profile', description: 'Provisioning Profile:'.prompt, required: true }
231+
], argv)
232+
.then(function(result) {
233+
q.resolve(result);
234+
})
235+
.catch(function(ex) {
236+
q.reject(ex);
237+
});
238+
} else {
239+
q.reject('Unrecognized platform: ' + platform);
240+
}
241+
242+
return q.promise;
243+
})
244+
.then(function(result) {
245+
if (platform === 'android') {
246+
var keystoreFilePath = path.resolve(expandTilde(result['keystore'])),
247+
keystorePassword = path.resolve(expandTilde(result['keystore-password'])),
248+
keyAlias = result['key-alias'],
249+
keyPassword = result['key-password'],
250+
keystoreFileStream = fs.createReadStream(keystoreFilePath);
251+
252+
return Security.addAndroidCredentials(appId, jar, profile, keystoreFileStream, keystorePassword, keyAlias, keyPassword)
253+
} else if (platform === 'ios') {
254+
var certificateFilePath = path.resolve(expandTilde(result['cert'])),
255+
certificatePassword = result['cert-password'],
256+
provisioningProfileFilePath = path.resolve(expandTilde(result['provisioning-profile'])),
257+
certificateFileStream = fs.createReadStream(certificateFilePath),
258+
provisioningProfileFileStream = fs.createReadStream(provisioningProfileFilePath);
259+
260+
return Security.addIOSCredentials(appId, jar, profile, certificateFileStream, certificatePassword, provisioningProfileFileStream)
261+
}
262+
263+
return Q.reject('Unrecognized platform.');
264+
})
265+
.then(function(body) {
266+
console.success('Added ' + platform + ' credentials to your Security Profile.');
267+
}, function(err) {
268+
if (typeof err === 'object') {
269+
if (err.type == 'CredentialsExist') {
270+
console.error(err.title);
271+
} else {
272+
return Q.reject(new Error(err.title));
273+
}
274+
}
275+
})
276+
.catch(function(ex) {
277+
Utils.fail(ex, 'package');
278+
});
279+
280+
};
281+
282+
exports.IonicTask = IonicTask;

lib/tasks/cliTasks.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,26 @@ var TASKS = [
289289
},
290290
module: './ionic/io-init'
291291
},
292+
{
293+
title: 'security',
294+
name: 'security',
295+
summary: 'Store your app\'s credentials for the Ionic Platform ' + '(alpha)'.red,
296+
args: {
297+
'<command>': 'profiles list'.yellow + ', ' + 'profiles add "<name>"'.yellow + ', ' + 'credentials android'.yellow + ', or ' + 'credentials ios'.yellow,
298+
'[options]': ''
299+
},
300+
options: {
301+
'--profile <tag>': '(' + 'credentials <platform>'.yellow + ') Specify the profile on which these credentials are saved',
302+
'--keystore|-s <path>': '(' + 'credentials android'.yellow + ') Specify the location of your keystore file',
303+
'--keystore-password|-p <password>': '(' + 'credentials android'.yellow + ') Specify your keystore password (exclude for prompt)',
304+
'--key-alias|-k <alias>': '(' + 'credentials android'.yellow + ') Specify your key alias for this app',
305+
'--key-password|-w <password>': '(' + 'credentials android'.yellow + ') Specify your key password for this app (exclude for prompt)',
306+
'--cert|-c <path>': '(' + 'credentials ios'.yellow + ') Specify the location of your .p12 file',
307+
'--cert-password|-p <password>': '(' + 'credentials ios'.yellow + ') Specify your certificate password (exclude for prompt)',
308+
'--provisioning-profile|-r <path>': '(' + 'credentials ios'.yellow + ') Specify the location of your .mobileprovision file',
309+
},
310+
module: './ionic/security'
311+
},
292312
{
293313
title: 'push',
294314
name: 'push',

0 commit comments

Comments
 (0)