Skip to content

Commit

Permalink
Merge pull request #1496 from atsign-foundation/apkam_spec_changes
Browse files Browse the repository at this point in the history
feat: apkam spec changes and keys verb impl
  • Loading branch information
gkc authored Aug 23, 2023
2 parents 60b54f9 + f07cbc5 commit d950575
Show file tree
Hide file tree
Showing 30 changed files with 3,553 additions and 888 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ class InboundConnectionMetadata extends AtConnectionMetaData {
String? platform;

/// A unique identifier generated for a client's APKAM enroll request
String? enrollApprovalId;
String? enrollmentId;
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
const String enrollManageNamespace = '__manage';
const String newEnrollmentKeyPattern = 'new.enrollments';
const String pkamNamespace = '__pkams';
const String globalNamespace = '__global';
const String allNamespaces = '*';
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ class EnrollDataStoreValue {
late String sessionId;
late String appName;
late String deviceName;
List<EnrollNamespace> namespaces = [];
// map for representing namespace access. key will be the namespace, value will be the access
// e.g {'wavi':'r', 'buzz':'rw'}
Map<String, String> namespaces = {};
late String apkamPublicKey;
EnrollRequestType? requestType;
EnrollApproval? approval;
Expand All @@ -21,24 +23,6 @@ class EnrollDataStoreValue {
Map<String, dynamic> toJson() => _$EnrollDataStoreValueToJson(this);
}

class EnrollNamespace {
String name;
String access;
EnrollNamespace(this.name, this.access);
EnrollNamespace.fromJson(Map<String, dynamic> json)
: name = json['name'],
access = json['access'];
Map<String, dynamic> toJson() => {
'name': name,
'access': access,
};

@override
String toString() {
return '{name: $name, access: $access}';
}
}

class EnrollApproval {
String state;
EnrollApproval(this.state);
Expand Down

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

Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class GlobalExceptionHandler {
exception is HandShakeException ||
exception is UnAuthenticatedException ||
exception is UnAuthorizedException ||
exception is AtEnrollmentException ||
exception is OutBoundConnectionInvalidException ||
exception is KeyNotFoundException ||
exception is AtConnectException ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ abstract class AbstractUpdateVerbHandler extends ChangeVerbHandler {

final enrollApprovalId =
(atConnection.getMetaData() as InboundConnectionMetadata)
.enrollApprovalId;
.enrollmentId;
bool isAuthorized = true; // for legacy clients allow access by default
if (enrollApprovalId != null) {
if (atKey.contains('.')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,6 @@ abstract class AbstractVerbHandler implements VerbHandler {
Future<void> processVerb(Response response,
HashMap<String, String?> verbParams, InboundConnection atConnection);

Future<List<EnrollNamespace>> getEnrollmentNamespaces(
String enrollmentId, String currentAtSign) async {
final key = '$enrollmentId.$newEnrollmentKeyPattern.$enrollManageNamespace';
EnrollDataStoreValue enrollDataStoreValue;
try {
enrollDataStoreValue =
await getEnrollDataStoreValue('$key$currentAtSign');
} on KeyNotFoundException {
logger.warning('enrollment key not found in keystore $key');
return [];
}
logger.finer('scan namespaces: ${enrollDataStoreValue.namespaces}');
return enrollDataStoreValue.namespaces;
}

/// Fetch for an enrollment key in the keystore.
/// If key is available returns [EnrollDataStoreValue],
/// else throws [KeyNotFoundException]
Expand All @@ -111,43 +96,55 @@ abstract class AbstractVerbHandler implements VerbHandler {
}
}

Future<bool> isAuthorized(
String enrollApprovalId, String keyNamespace) async {
EnrollDataStoreValue enrollDataStoreValue;
final enrollmentKey =
'$enrollApprovalId.$newEnrollmentKeyPattern.$enrollManageNamespace';
/// Verifies whether the enrollment namespace for the enrollment
/// ID has the necessary permissions to modify, delete, or retrieve the data.
/// The enrollment should be in an approved state.
///
/// To execute a data retrieval (lookup or local lookup), the namespace must have
/// "r" (read) privileges within the namespace.
/// For update or delete actions, the namespace must have "rw" (read-write) privileges.
///
/// Returns true, if the namespace has the required read or read-write
/// permissions to execute lookup/local-lookup or update/delete operations
/// respectively
///
/// Returns false
/// - If the enrollment key is not present in the keystore.
/// - If the enrollment is not in "approved" state
/// - If the namespace does not have necessary permissions to perform the operation
/// - If enrollment is a part of "global" or "manage" namespace
Future<bool> isAuthorized(String enrollmentId, String keyNamespace) async {
try {
enrollDataStoreValue = await getEnrollDataStoreValue(
'$enrollmentKey${AtSecondaryServerImpl.getInstance().currentAtSign}');
} on KeyNotFoundException {
// When a key with enrollmentId is not found, atSign is not authorized to
// perform enrollment actions. Return false.
return false;
}
final enrollmentKey =
'$enrollmentId.$newEnrollmentKeyPattern.$enrollManageNamespace';
final fullKey =
'$enrollmentKey${AtSecondaryServerImpl.getInstance().currentAtSign}';

if (enrollDataStoreValue.approval?.state != EnrollStatus.approved.name) {
return false;
}
final enrollDataStoreValue = await getEnrollDataStoreValue(fullKey);

final enrollNamespaces = await getEnrollmentNamespaces(
enrollApprovalId, AtSecondaryServerImpl.getInstance().currentAtSign);
if (enrollDataStoreValue.approval?.state != EnrollStatus.approved.name) {
return false;
}

logger.finer(
'keyNamespace: $keyNamespace enrollNamespaces: $enrollNamespaces');
for (EnrollNamespace namespace in enrollNamespaces) {
if (namespace.name == keyNamespace) {
logger.finer('current verb: ${getVerb()}');
if (getVerb() is LocalLookup || getVerb() is Lookup) {
if (namespace.access == 'r' || namespace.access == 'rw') {
return true;
}
} else if (getVerb() is Update || getVerb() is Delete) {
if (namespace.access == 'rw') {
return true;
}
final enrollNamespaces = enrollDataStoreValue.namespaces;
logger.finer('enrollNamespaces:$enrollNamespaces');
logger.finer('keyNamespace:$keyNamespace');
final access = enrollNamespaces.containsKey(allNamespaces)
? enrollNamespaces[allNamespaces]
: enrollNamespaces[keyNamespace];
logger.finer('access:$access');
if (keyNamespace != enrollManageNamespace && access != null) {
final verb = getVerb();
if ((verb is LocalLookup || verb is Lookup) &&
(access == 'r' || access == 'rw')) {
return true;
} else if ((verb is Update || verb is Delete) && access == 'rw') {
return true;
}
}
return false;
} on KeyNotFoundException {
return false;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ class DeleteVerbHandler extends ChangeVerbHandler {
}
// Sets Response bean to the response bean in ChangeVerbHandler
await super.processVerb(response, verbParams, atConnection);
var keyNamespace = deleteKey.substring(deleteKey.lastIndexOf('.') + 1);
var keyNamespace =
verbParams[AT_KEY]!.substring(deleteKey.lastIndexOf('.') + 1);
if (verbParams[FOR_AT_SIGN] != null) {
deleteKey = '${AtUtils.formatAtSign(verbParams[FOR_AT_SIGN])}:$deleteKey';
}
Expand All @@ -88,8 +89,7 @@ class DeleteVerbHandler extends ChangeVerbHandler {
await keyStore.put(AT_CRAM_SECRET_DELETED, AtData()..data = 'true');
}
final enrollApprovalId =
(atConnection.getMetaData() as InboundConnectionMetadata)
.enrollApprovalId;
(atConnection.getMetaData() as InboundConnectionMetadata).enrollmentId;
bool isAuthorized = true; // for legacy clients allow access by default
if (enrollApprovalId != null) {
isAuthorized = await super.isAuthorized(enrollApprovalId, keyNamespace);
Expand Down
Loading

0 comments on commit d950575

Please sign in to comment.