Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: APKAM keys expiry feature changes in at_onboarding_cli #644

2 changes: 2 additions & 0 deletions packages/at_auth/lib/src/enroll/at_enrollment_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ class AtEnrollmentImpl implements AtEnrollmentBase {
.encrypt(apkamSymmetricKey.key);
enrollVerbBuilder.otp = enrollmentRequest.otp;
enrollVerbBuilder.namespaces = enrollmentRequest.namespaces;
enrollVerbBuilder.apkamKeysExpiryDuration =
enrollmentRequest.apkamKeysExpiryDuration;

String? serverResponse =
await _executeEnrollCommand(enrollVerbBuilder, atLookUp);
Expand Down
4 changes: 3 additions & 1 deletion packages/at_auth/lib/src/enroll/enrollment_request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ class EnrollmentRequest extends BaseEnrollmentRequest {
Map<String, String> namespaces;
String? encryptedAPKAMSymmetricKey;
String otp;
Duration? apkamKeysExpiryDuration;

EnrollmentRequest(
{required super.appName,
required super.deviceName,
super.apkamPublicKey,
required this.otp,
required this.namespaces,
this.encryptedAPKAMSymmetricKey});
this.encryptedAPKAMSymmetricKey,
this.apkamKeysExpiryDuration});
}
5 changes: 4 additions & 1 deletion packages/at_commons/lib/src/verb/enroll_params.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:at_commons/at_commons.dart';
import 'package:json_annotation/json_annotation.dart';

import 'package:at_commons/at_commons.dart';
part 'enroll_params.g.dart';

@JsonSerializable()
Expand All @@ -15,7 +15,10 @@ class EnrollParams {
String? encryptedAPKAMSymmetricKey;
String? apkamPublicKey;
List<EnrollmentStatus>? enrollmentStatusFilter;
Duration? apkamKeysExpiryDuration;

EnrollParams();

factory EnrollParams.fromJson(Map<String, dynamic> json) =>
_$EnrollParamsFromJson(json);

Expand Down
8 changes: 6 additions & 2 deletions packages/at_commons/lib/src/verb/enroll_params.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion packages/at_commons/lib/src/verb/enroll_verb_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class EnrollVerbBuilder extends AbstractVerbBuilder {
/// Accepts a list of enrollment statuses. Defaults to all EnrollmentStatuses
List<EnrollmentStatus>? enrollmentStatusFilter;

Duration? apkamKeysExpiryDuration;

@override
String buildCommand() {
var sb = StringBuffer();
Expand All @@ -64,7 +66,8 @@ class EnrollVerbBuilder extends AbstractVerbBuilder {
encryptedDefaultEncryptionPrivateKey
..encryptedDefaultSelfEncryptionKey = encryptedDefaultSelfEncryptionKey
..encryptedAPKAMSymmetricKey = encryptedAPKAMSymmetricKey
..enrollmentStatusFilter = enrollmentStatusFilter;
..enrollmentStatusFilter = enrollmentStatusFilter
..apkamKeysExpiryDuration = apkamKeysExpiryDuration;

Map<String, dynamic> enrollParamsJson = enrollParams.toJson();
enrollParamsJson.removeWhere(_removeElements);
Expand Down
43 changes: 31 additions & 12 deletions packages/at_onboarding_cli/lib/src/cli/auth_cli.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import 'dart:convert';
import 'dart:io';

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_lookup/at_lookup.dart';
import 'package:at_onboarding_cli/at_onboarding_cli.dart';
import 'package:args/args.dart';
import 'package:at_onboarding_cli/src/util/at_onboarding_exceptions.dart';
import 'package:at_onboarding_cli/src/util/print_full_parser_usage.dart';
import 'dart:io';
import 'package:at_utils/at_utils.dart';
import 'package:duration/duration.dart';
import 'package:meta/meta.dart';

import 'auth_cli_args.dart';
import 'auth_cli_arg_validation.dart';
import 'auth_cli_args.dart';

final AtSignLogger logger = AtSignLogger(' CLI ');

Expand Down Expand Up @@ -334,12 +335,17 @@ Future<void> enroll(ArgResults argResults, {AtOnboardingService? svc}) async {
}
try {
stderr.writeln('Submitting enrollment request');
String apkamKeysExpiry = (argResults[AuthCliArgs.argNameExpiry] == null)
// If apkam Keys expiry is not set, then APKAM keys should lives forever.
// Therefore set to 0ms (0 milliseconds) and TTL will not be set.
? '0ms'
: argResults[AuthCliArgs.argNameExpiry];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use ?? syntax i.e. String apkamKeysExpiry = argResults[AuthCliArgs.argNameExpiry] ?? '0ms'

AtEnrollmentResponse er = await svc.sendEnrollRequest(
argResults[AuthCliArgs.argNameAppName],
argResults[AuthCliArgs.argNameDeviceName],
argResults[AuthCliArgs.argNamePasscode],
namespaces,
);
argResults[AuthCliArgs.argNameAppName],
argResults[AuthCliArgs.argNameDeviceName],
argResults[AuthCliArgs.argNamePasscode],
namespaces,
apkamKeysExpiryDuration: parseDuration(apkamKeysExpiry));
stdout.writeln('Enrollment ID: ${er.enrollmentId}');

stderr.writeln('Waiting for approval; will check every 10 seconds');
Expand Down Expand Up @@ -374,25 +380,38 @@ Future<void> enroll(ArgResults argResults, {AtOnboardingService? svc}) async {
@visibleForTesting
Future<void> setSpp(ArgResults argResults, AtClient atClient) async {
String spp = argResults[AuthCliArgs.argNameSpp];
String? sppExpiry = argResults[AuthCliArgs.argNameExpiry];
if (invalidSpp(spp)) {
throw ArgumentError(invalidSppMsg);
}

AtLookUp atLookup = atClient.getRemoteSecondary()!.atLookUp;
StringBuffer sppCommandBuffer = StringBuffer()..append('otp:put:$spp');
if (sppExpiry != null && sppExpiry.isNotEmpty) {
sppCommandBuffer.append(':ttl:${parseDuration(sppExpiry).inMilliseconds}');
}
sppCommandBuffer.append('\n');

AtLookUp atLookup = atClient.getRemoteSecondary()!.atLookUp;
// send command 'otp:put:$spp'
String? response =
await atLookup.executeCommand('otp:put:$spp\n', auth: true);
await atLookup.executeCommand(sppCommandBuffer.getData()!, auth: true);

stdout.writeln('Server response: $response');
}

@visibleForTesting
Future<void> generateOtp(ArgResults argResults, AtClient atClient) async {
AtLookUp atLookup = atClient.getRemoteSecondary()!.atLookUp;
String? otpExpiry = argResults[AuthCliArgs.argNameExpiry];
StringBuffer otpCommandBuffer = StringBuffer()..append('otp:get');
if (otpExpiry != null && otpExpiry.isNotEmpty) {
otpCommandBuffer.append(':ttl:${parseDuration(otpExpiry).inMilliseconds}');
}
otpCommandBuffer.append('\n');

AtLookUp atLookup = atClient.getRemoteSecondary()!.atLookUp;
// send command 'otp:get[:ttl:$ttl]'
String? response = await atLookup.executeCommand('otp:get\n', auth: true);
String? response =
await atLookup.executeCommand(otpCommandBuffer.getData()!, auth: true);
if (response != null && response.startsWith('data:')) {
stdout.writeln(response.substring('data:'.length));
} else {
Expand Down
17 changes: 17 additions & 0 deletions packages/at_onboarding_cli/lib/src/cli/auth_cli_args.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ enum AuthCliCommand {
' program which has "rw" access to the "__manage" namespace.');

const AuthCliCommand({this.usage = ''});

final String usage;
}

Expand Down Expand Up @@ -94,6 +95,7 @@ class AuthCliArgs {
static const argNameAppNameRegex = 'arx';
static const argNameDeviceNameRegex = 'drx';
static const argNameMaxConnectAttempts = 'mca';
static const argNameExpiry = 'expiry';

ArgParser get parser {
return _aap;
Expand Down Expand Up @@ -275,6 +277,11 @@ class AuthCliArgs {
help: 'The semi-permanent enrollment passcode to set for this atSign',
mandatory: true,
);
p.addOption(argNameExpiry,
abbr: 'e',
help:
'The duration for which the SPP remains active. The time duration can be passed as "2d,1h,10m,20s,999ms" for 2 days 1 hour 10 minutes 20 seconds 999 milliseconds',
mandatory: false);
return p;
}

Expand All @@ -288,6 +295,11 @@ class AuthCliArgs {
@visibleForTesting
ArgParser createOtpCommandParser() {
ArgParser p = createSharedArgParser(hide: true);
p.addOption(argNameExpiry,
abbr: 'e',
help:
'The duration for which the OTP remains active. The time duration can be passed as "2d,1h,10m,20s,999ms" for 2 days 1 hour 10 minutes 20 seconds 999 milliseconds',
mandatory: false);
return p;
}

Expand Down Expand Up @@ -324,6 +336,11 @@ class AuthCliArgs {
' e.g. "buzz:rw,contacts:rw,__manage:rw"',
mandatory: true,
);
p.addOption(argNameExpiry,
abbr: 'e',
help:
'The duration for which the APKAM keys remains active. The time duration can be passed as "2d,1h,10m,20s,999ms" for 2 days 1 hour 10 minutes 20 seconds 999 milliseconds',
mandatory: false);
return p;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:io';

import 'package:at_auth/at_auth.dart';
import 'package:at_chops/at_chops.dart';
import 'package:at_client/at_client.dart';
import 'package:at_lookup/at_lookup.dart';
import 'package:at_auth/at_auth.dart';

abstract class AtOnboardingService {
static const Duration defaultApkamRetryInterval = Duration(seconds: 10);
Expand Down Expand Up @@ -44,12 +44,9 @@ abstract class AtOnboardingService {

/// Sends enrollment request. Application code may subsequently call
/// [awaitApproval].
Future<AtEnrollmentResponse> sendEnrollRequest(
String appName,
String deviceName,
String otp,
Map<String, String> namespaces,
);
Future<AtEnrollmentResponse> sendEnrollRequest(String appName,
String deviceName, String otp, Map<String, String> namespaces,
{Duration? apkamKeysExpiryDuration});

/// Attempts PKAM auth until successful (i.e. request was approved).
/// If the request was denied, or times out, then an exception is thrown.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,24 +194,23 @@ class AtOnboardingServiceImpl implements AtOnboardingService {
}

@override
Future<at_auth.AtEnrollmentResponse> sendEnrollRequest(
String appName,
String deviceName,
String otp,
Map<String, String> namespaces,
) async {
Future<at_auth.AtEnrollmentResponse> sendEnrollRequest(String appName,
String deviceName, String otp, Map<String, String> namespaces,
{Duration? apkamKeysExpiryDuration}) async {
if (appName == null || deviceName == null) {
throw AtEnrollmentException(
'appName and deviceName are mandatory for enrollment');
}

at_auth.EnrollmentRequest newClientEnrollmentRequest =
at_auth.EnrollmentRequest(
appName: appName,
deviceName: deviceName,
namespaces: namespaces,
otp: otp,
);
appName: appName,
deviceName: deviceName,
namespaces: namespaces,
otp: otp);
newClientEnrollmentRequest.apkamKeysExpiryDuration =
apkamKeysExpiryDuration;

AtLookupImpl atLookUpImpl = AtLookupImpl(_atSign,
atOnboardingPreference.rootDomain, atOnboardingPreference.rootPort);
logger.finer('sendEnrollRequest: submitting enrollment request');
Expand Down
47 changes: 30 additions & 17 deletions packages/at_onboarding_cli/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,36 @@ executables:
at_activate: activate_cli

dependencies:
args: ^2.5.0
crypton: ^2.2.1
encrypt: ^5.0.3
http: ^1.2.1
image: ^4.1.7
meta: ^1.14.0
path: ^1.9.0
zxing2: ^0.2.0
at_auth: ^2.0.5
at_chops: ^2.0.0
at_client: ^3.0.76
at_commons: ^4.0.11
at_lookup: ^3.0.47
at_server_status: ^1.0.4
at_utils: ^3.0.16
at_cli_commons: ^1.1.0
at_persistence_secondary_server: ^3.0.62
args: ^2.5.0
crypton: ^2.2.1
encrypt: ^5.0.3
http: ^1.2.1
image: ^4.1.7
meta: ^1.14.0
path: ^1.9.0
zxing2: ^0.2.0
at_auth: ^2.0.5
at_chops: ^2.0.0
at_client: ^3.0.76
at_commons: ^4.0.11
at_lookup: ^3.0.47
at_server_status: ^1.0.4
at_utils: ^3.0.16
at_cli_commons: ^1.1.0
at_persistence_secondary_server: ^3.0.62
duration: ^4.0.3

dependency_overrides:
at_commons:
git:
url: https://github.com/atsign-foundation/at_libraries.git
path: packages/at_commons
ref: 2074-introducing-auto-expiry-and-time-to-birth-features-for-apkam-keys
at_auth:
git:
url: https://github.com/atsign-foundation/at_libraries.git
path: packages/at_auth
ref: 2074-introducing-auto-expiry-and-time-to-birth-features-for-apkam-keys

dev_dependencies:
lints: ^2.1.0
Expand Down
2 changes: 2 additions & 0 deletions tests/at_onboarding_cli_functional_tests/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ dependency_overrides:
path: ../../packages/at_auth
at_onboarding_cli:
path: ../../packages/at_onboarding_cli
at_commons:
path: ../../packages/at_commons

dev_dependencies:
lints: ^1.0.0
Expand Down