Skip to content

Commit 61b090a

Browse files
committed
Merge pull request #334 from uProxy/trevj-inviteUser
implement inviteUser in the cloud social provider
2 parents c8c7e36 + fd0aea1 commit 61b090a

File tree

1 file changed

+36
-6
lines changed

1 file changed

+36
-6
lines changed

src/cloud/social/provider.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ const ZORK_PORT = 9000;
2222
// Key under which our contacts are saved in storage.
2323
const STORAGE_KEY = 'cloud-social-contacts';
2424

25+
// Admin users can issue invites.
26+
const ADMIN_USERNAME = 'giver';
27+
const REGULAR_USERNAME = 'getter';
28+
2529
// Credentials for accessing a cloud instance.
2630
// The serialised, base64 form is distributed amongst users.
2731
// TODO: add (private) keys, for key-based auth
@@ -335,9 +339,22 @@ export class CloudSocialProvider {
335339
// social2
336340
////
337341

338-
public inviteUser = (inviteCode: string): Promise<void> => {
339-
return Promise.reject(
340-
new Error('inviteUser unimplemented'));
342+
// Returns a new invite code for the specified server.
343+
// Rejects if the user is not logged in as an admin or there
344+
// is any error executing the command.
345+
// TODO: typings for invite codes
346+
public inviteUser = (clientId: string): Promise<Object> => {
347+
log.debug('inviteUser');
348+
if (!(clientId in this.clients_)) {
349+
return Promise.reject(new Error('unknown client ' + clientId));
350+
}
351+
if (this.savedContacts_[clientId].invite.user !== ADMIN_USERNAME) {
352+
return Promise.reject(new Error('user is logged in as non-admin user ' +
353+
this.savedContacts_[clientId].invite.user));
354+
}
355+
return this.clients_[clientId].then((connection: Connection) => {
356+
return connection.issueInvite();
357+
});
341358
}
342359

343360
// Parses an invite code, received from uProxy in JSON format.
@@ -526,11 +543,24 @@ class Connection {
526543

527544
// Fetches the server's description, i.e. /banner.
528545
public getBanner = (): Promise<string> => {
546+
return this.exec_('cat /banner');
547+
}
548+
549+
// Returns a base64-decoded, deserialised invite code.
550+
public issueInvite = (): Promise<Object> => {
551+
return this.exec_('sudo /issue_invite.sh').then((inviteCode: string) => {
552+
return JSON.parse(new Buffer(inviteCode, 'base64').toString());
553+
});
554+
}
555+
556+
// Executes a command, fulfilling with the command's stdout
557+
// or rejecting if output is received on stderr.
558+
private exec_ = (command:string): Promise<string> => {
529559
if (this.state_ !== ConnectionState.ESTABLISHED) {
530-
return Promise.reject(new Error('can only fetch banner in ESTABLISHED state'));
560+
return Promise.reject(new Error('can only execute commands in ESTABLISHED state'));
531561
}
532562
return new Promise<string>((F, R) => {
533-
this.client_.exec('cat /banner', (e: Error, stream: ssh2.Channel) => {
563+
this.client_.exec(command, (e: Error, stream: ssh2.Channel) => {
534564
if (e) {
535565
R(e);
536566
return;
@@ -540,7 +570,7 @@ class Connection {
540570
}).stderr.on('data', function(data: Buffer) {
541571
R(new Error(data.toString()));
542572
}).on('close', function(code: any, signal: any) {
543-
log.debug('banner stream closed');
573+
log.debug('exec stream closed');
544574
});
545575
});
546576
});

0 commit comments

Comments
 (0)