Skip to content

Commit

Permalink
Merge pull request #682 from atsign-foundation/unrevoke_at_onboarding…
Browse files Browse the repository at this point in the history
…_cli_changes

fix: Add unrevoke to the at_onboarding_cli
  • Loading branch information
murali-shris authored Oct 15, 2024
2 parents 7976f8b + 53c3288 commit ea41c56
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 45 deletions.
3 changes: 3 additions & 0 deletions packages/at_onboarding_cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 1.8.0
- feat: add `unrevoke` command to the activate CLI
- feat: add `delete` command to the activate CLI
## 1.7.0
- feat: add `auto` command to the activate CLI
## 1.6.4
Expand Down
123 changes: 84 additions & 39 deletions packages/at_onboarding_cli/lib/src/cli/auth_cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:args/args.dart';
import 'package:at_auth/at_auth.dart';
import 'package:at_cli_commons/at_cli_commons.dart';
import 'package:at_client/at_client.dart';
import 'package:at_commons/at_builders.dart';
import 'package:at_lookup/at_lookup.dart';
import 'package:at_onboarding_cli/at_onboarding_cli.dart';
import 'package:at_onboarding_cli/src/util/at_onboarding_exceptions.dart';
Expand Down Expand Up @@ -195,6 +196,14 @@ Future<int> wrappedMain(List<String> arguments) async {
// Write keys to @atSign_keys.atKeys IFF it doesn't already exist; if
// it does exist, then write to @atSign_appName_deviceName_keys.atKeys
await enroll(commandArgResults);

case AuthCliCommand.unrevoke:
await unrevoke(
commandArgResults, await createAtClient(commandArgResults));

case AuthCliCommand.delete:
await deleteEnrollment(
commandArgResults, await createAtClient(commandArgResults));
}
} on ArgumentError catch (e) {
stderr
Expand Down Expand Up @@ -272,10 +281,6 @@ Future<int> status(ArgResults ar) async {
}

Future<AtClient> createAtClient(ArgResults ar) async {
if (storageDir != null) {
throw StateError('AtClient has already been created');
}

String nameSpace = 'at_activate';
String atSign = AtUtils.fixAtSign(ar[AuthCliArgs.argNameAtSign]);
storageDir = standardAtClientStorageDir(
Expand Down Expand Up @@ -317,24 +322,27 @@ Future<void> onboard(ArgResults argResults, {AtOnboardingService? svc}) async {
'[Information] Onboarding your atSign. This may take up to 2 minutes.');
try {
await svc.onboard();
logger.finest('svc.onboard() has returned - will exit(0)');
exit(0);
return;
} on InvalidDataException catch (e) {
stderr.writeln(
'[Error] Onboarding failed. Invalid data provided by user. Please try again\nCause: ${e.message}');
exit(1);
throw AtEnrollmentException(
'Onboarding failed. Please try again. Cause: ${e.message}');
} on InvalidRequestException catch (e) {
stderr.writeln(
'[Error] Onboarding failed. Invalid data provided by user. Please try again\nCause: ${e.message}');
exit(1);
} on AtActivateException catch (e) {
stderr.writeln('[Error] ${e.message}');
exit(1);
} on Exception catch (e) {
stderr.writeln(
'[Error] Onboarding failed. It looks like something went wrong on our side.\n'
'Please try again or contact [email protected]\nCause: $e');
exit(1);
throw AtEnrollmentException(
'Onboarding failed. Please try again. Cause: ${e.message}');
} on AtActivateException {
rethrow;
} catch (e) {
throw ('Onboarding failed.'
' It looks like something went wrong on our side.'
' Please try again or contact [email protected]\nCause: $e');
}
}

String parseServerResponse(String? response) {
if (response != null && response.startsWith('data:')) {
return response.replaceFirst('data:', '');
} else {
throw ('Unexpected server response: $response');
}
}

Expand Down Expand Up @@ -535,6 +543,12 @@ Future<void> interactive(ArgResults argResults, AtClient atClient) async {

case AuthCliCommand.revoke:
await revoke(commandArgResults, atClient);

case AuthCliCommand.unrevoke:
await unrevoke(commandArgResults, atClient);

case AuthCliCommand.delete:
await deleteEnrollment(commandArgResults, atClient);
}
} on ArgumentError catch (e) {
stderr.writeln(
Expand Down Expand Up @@ -590,8 +604,7 @@ Future<Map> _list(
stdout.writeln("Found ${filtered.length} matching enrollment records");
return filtered;
} else {
stderr.writeln('Exiting: Unexpected server response: $rawResponse');
exit(1);
throw Exception('Unexpected server response: $rawResponse');
}
}

Expand Down Expand Up @@ -623,20 +636,14 @@ Future<void> list(ArgResults ar, AtClient atClient) async {
}

Future<Map?> _fetch(String eId, AtLookUp atLookup) async {
String rawResponse = (await atLookup.executeCommand(
'enroll:fetch:'
'{"enrollmentId":"$eId"}'
'\n',
auth: true))!;

if (rawResponse.startsWith('data:')) {
rawResponse = rawResponse.substring(rawResponse.indexOf('data:') + 5);
// response is a Map
return jsonDecode(rawResponse);
} else {
stderr.writeln('Exiting: Unexpected server response: $rawResponse');
exit(1);
}
EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder()
..operation = EnrollOperationEnum.fetch
..enrollmentId = eId;
String? response = await atLookup.executeVerb(enrollVerbBuilder);

response = parseServerResponse(response);
// response is a Map
return jsonDecode(response);
}

Future<void> fetch(ArgResults argResults, AtClient atClient) async {
Expand Down Expand Up @@ -853,9 +860,10 @@ Future<void> deny(ArgResults ar, AtClient atClient) async {
// Iterate through the requests, deny each one
for (String eId in toDeny.keys) {
stdout.writeln('Denying enrollmentId $eId');
// 'enroll:deny:{"enrollmentId":"$enrollmentId"}'
String? response = await atLookup
.executeCommand('enroll:deny:{"enrollmentId":"$eId"}\n', auth: true);
EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder()
..operation = EnrollOperationEnum.deny
..enrollmentId = eId;
String? response = await atLookup.executeVerb(enrollVerbBuilder);
stdout.writeln('Server response: $response');
}
}
Expand Down Expand Up @@ -886,6 +894,43 @@ Future<void> revoke(ArgResults ar, AtClient atClient) async {
}
}

Future<void> unrevoke(ArgResults ar, AtClient atClient) async {
AtLookUp atLookup = atClient.getRemoteSecondary()!.atLookUp;

Map toUnRevoke = await _fetchOrListAndFilter(
atLookup,
EnrollmentStatus.approved.name, // must be status approved
eId: ar[AuthCliArgs.argNameEnrollmentId],
arx: ar[AuthCliArgs.argNameAppNameRegex],
drx: ar[AuthCliArgs.argNameDeviceNameRegex],
);

if (toUnRevoke.isEmpty) {
stderr.writeln('No matching enrollment(s) found');
return;
}

for (String eId in toUnRevoke.keys) {
stdout.writeln('Un-Revoking enrollmentId $eId');
String? response = await atLookup.executeCommand(
'enroll:unrevoke:{"enrollmentId":"$eId"}\n',
auth: true);
stdout.writeln('Server response: $response');
}
}

Future<void> deleteEnrollment(ArgResults ar, AtClient atClient) async {
AtLookUp atLookup = atClient.getRemoteSecondary()!.atLookUp;
String eId = ar[AuthCliArgs.argNameEnrollmentId];
EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder()
..enrollmentId = eId
..operation = EnrollOperationEnum.delete;
stdout.writeln('Sending delete request');
String? response = await atLookup.executeVerb(enrollVerbBuilder);
response = parseServerResponse(response);
stdout.writeln('Server response: $response');
}

@visibleForTesting
AtOnboardingService createOnboardingService(ArgResults ar) {
String atSign = AtUtils.fixAtSign(ar[AuthCliArgs.argNameAtSign]);
Expand Down
38 changes: 34 additions & 4 deletions packages/at_onboarding_cli/lib/src/cli/auth_cli_args.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,16 @@ enum AuthCliCommand {
list(usage: 'List enrollment requests'),
fetch(usage: 'Fetch a specific enrollment request'),
approve(usage: 'Approve a pending enrollment request'),
auto(usage: 'Listen for new enrollment requests which match the parameters'
' supplied, and auto-approve them. Will exit after N (defaults to 1)'
' enrollment requests have been approved.'),
auto(
usage: 'Listen for new enrollment requests which match the parameters'
' supplied, and auto-approve them. Will exit after N (defaults to 1)'
' enrollment requests have been approved.'),
deny(usage: 'Deny a pending enrollment request'),
revoke(usage: 'Revoke approval of a previously-approved enrollment'),
unrevoke(usage: 'Restores access to the previously revoked enrollment'),
delete(
usage: 'Deletes an enrollment. Requires an enrollmentId to be provided'
'\nNOTE: Can ONLY delete denied and revoked enrollments'),
enroll(
usage: 'Enroll is used when a program needs to authenticate and'
' "atKeys" are not available, and "onboard" has already been run'
Expand Down Expand Up @@ -182,6 +187,12 @@ class AuthCliArgs {

case AuthCliCommand.revoke:
return createRevokeCommandParser();

case AuthCliCommand.unrevoke:
return createUnRevokeCommandParser();

case AuthCliCommand.delete:
return createDeleteCommandParser();
}
}

Expand Down Expand Up @@ -427,7 +438,7 @@ class AuthCliArgs {
@visibleForTesting
ArgParser createAutoApproveCommandParser() {
ArgParser p = createSharedArgParser(hide: true);
_addEnrollmentIdOption(p, hide:true);
_addEnrollmentIdOption(p, hide: true);
_addAppNameRegexOption(p, mandatory: true);
_addDeviceNameRegexOption(p, mandatory: true);
p.addOption(
Expand Down Expand Up @@ -467,4 +478,23 @@ class AuthCliArgs {
_addDeviceNameRegexOption(p, mandatory: false);
return p;
}

/// Restore the revoked enrollment Id.
@visibleForTesting
ArgParser createUnRevokeCommandParser() {
ArgParser p = createSharedArgParser(hide: true);
_addEnrollmentIdOption(p);
_addAppNameRegexOption(p, mandatory: false);
_addDeviceNameRegexOption(p, mandatory: false);
return p;
}

/// auth delete denied enrollment: requires enrollmentId and atKeysFile path
/// requires the enrollment to be denied
@visibleForTesting
ArgParser createDeleteCommandParser() {
ArgParser p = createSharedArgParser(hide: true);
_addEnrollmentIdOption(p, mandatory: true);
return p;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class OnboardingUtil {
'To register a new atSign to this email address, please log into the dashboard \'my.atsign.com/login\'.\n'
'Remove at least 1 atSign from your account and then try again.\n'
'Alternatively, you can retry this process with a different email address.');
exit(1);
throw at_client.AtClientException.message(jsonDecoded['message']);
} else {
throw at_client.AtClientException.message(
'${response.statusCode} ${jsonDecoded['message']}');
Expand Down
2 changes: 1 addition & 1 deletion packages/at_onboarding_cli/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: at_onboarding_cli
description: Dart tools for initial client onboarding, subsequent client enrollment, and enrollment management.
version: 1.7.0
version: 1.8.0
repository: https://github.com/atsign-foundation/at_libraries
homepage: https://atsign.com
documentation: https://docs.atsign.com/
Expand Down
Loading

0 comments on commit ea41c56

Please sign in to comment.