Skip to content

Commit

Permalink
Merge branch 'trunk' of https://github.com/atsign-foundation/at_tools
Browse files Browse the repository at this point in the history
…into exception_stack_test
  • Loading branch information
purnimavenkatasubbu committed Oct 19, 2022
2 parents bfbdb8c + 2d338d5 commit 4b93b98
Show file tree
Hide file tree
Showing 16 changed files with 329 additions and 9 deletions.
3 changes: 2 additions & 1 deletion at_cli/bin/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:at_utils/at_logger.dart';

void main(List<String> arguments) async {
AtSignLogger.root_level = 'severe';
await ConfigUtil.init();
var logger = AtSignLogger('AtCli');
try {
var parsedArgs = CommandLineParser.getParserResults(arguments);
Expand Down Expand Up @@ -82,4 +83,4 @@ Future<AtCliPreference> _getAtCliPreference(ArgResults? parsedArgs) async {
: ConfigUtil.getYaml()!['auth']['key_file_location'];

return preferences;
}
}
22 changes: 20 additions & 2 deletions at_cli/lib/src/config_util.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
import 'dart:io';
import 'dart:isolate';

import 'package:at_utils/at_utils.dart';
import 'package:yaml/yaml.dart';
import 'package:path/path.dart' as path;

class ConfigUtil {
static final ApplicationConfiguration appConfig =
ApplicationConfiguration('config/config.yaml');
static late final ApplicationConfiguration appConfig;

static Future<void> init() async {
String configPath = await _getConfigFile();
appConfig = ApplicationConfiguration(configPath);
}

static YamlMap? getYaml() {
return appConfig.getYaml();
}

static Future<String> _getConfigFile() async {
var fileUri = await Isolate.resolvePackageUri(
Uri.parse('package:at_cli/'));
if (fileUri == null) {
throw Exception('Could not find package location');
}
Directory packageRoot = Directory.fromUri(fileUri).parent;
return path.join(packageRoot.path, 'config/config.yaml');
}
}
2 changes: 2 additions & 0 deletions at_commons/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
## 3.0.27
* feat: Implement the `==` and `hashCode` methods for AtKey, AtValue and Metadata classes
## 3.0.26
* feat: Introduce notifyFetch verb
* fix: bug in at_exception_stack.dart
Expand Down
1 change: 1 addition & 0 deletions at_commons/lib/src/at_constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,4 @@ const String statsNotificationId = '_latestNotificationIdv2';
const String ENCODING = 'encoding';
const String CLIENT_CONFIG = 'clientConfig';
const String VERSION = 'version';
const String IS_LOCAL = 'isLocal';
136 changes: 136 additions & 0 deletions at_commons/lib/src/keystore/at_key.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,33 @@ class AtKey {
Metadata? metadata;
bool isRef = false;

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AtKey &&
runtimeType == other.runtimeType &&
key == other.key &&
_sharedWith == other._sharedWith &&
_sharedBy == other._sharedBy &&
namespace == other.namespace &&
metadata == other.metadata &&
isRef == other.isRef &&
_isLocal == other._isLocal;

@override
int get hashCode =>
key.hashCode ^
_sharedWith.hashCode ^
_sharedBy.hashCode ^
namespace.hashCode ^
metadata.hashCode ^
isRef.hashCode ^
_isLocal.hashCode;

/// When set to true, represents the [LocalKey]
/// These keys will never be synced between the client and secondary server.
bool _isLocal = false;

String? get sharedBy => _sharedBy;

set sharedBy(String? atSign) {
Expand All @@ -24,6 +51,16 @@ class AtKey {
_sharedWith = atSign;
}

bool get isLocal => _isLocal;

set isLocal(bool isLocal) {
if (isLocal == true && sharedWith != null) {
throw InvalidAtKeyException(
'sharedWith should be empty when isLocal is set to true');
}
_isLocal = isLocal;
}

String _dotNamespaceIfPresent() {
if (namespace != null && namespace!.isNotEmpty) {
return '.$namespace';
Expand Down Expand Up @@ -62,6 +99,14 @@ class AtKey {
if (_sharedWith != null && _sharedWith!.isNotEmpty) {
return '$_sharedWith:$key${_dotNamespaceIfPresent()}$_sharedBy';
}
// if key starts with local: or isLocal set to true, return local key
if (isLocal == true) {
String localKey = '$key${_dotNamespaceIfPresent()}$sharedBy';
if (localKey.startsWith('local:')) {
return localKey;
}
return 'local:$localKey';
}
// Defaults to return a self key.
return '$key${_dotNamespaceIfPresent()}$_sharedBy';
}
Expand Down Expand Up @@ -151,6 +196,23 @@ class AtKey {
..namespace(namespace);
}

/// Local key are confined to the client(device)/server it is created.
/// The key does not sync between the local-secondary and the cloud-secondary.
///
/// Builds a local key and return a [LocalKeyBuilder].
///
/// Example: local:phone.wavi@alice
/// ```dart
/// AtKey localKey = AtKey.local('phone',namespace:'wavi').build();
/// ```
static LocalKeyBuilder local(String key, String sharedBy,
{String? namespace}) {
return LocalKeyBuilder()
..key(key)
..namespace(namespace)
..sharedBy(sharedBy);
}

static AtKey fromString(String key) {
var atKey = AtKey();
var metaData = Metadata();
Expand Down Expand Up @@ -180,6 +242,9 @@ class AtKey {
if (keyParts[0] == 'public') {
metaData.isPublic = true;
}
if (keyParts[0] == 'local') {
atKey.isLocal = true;
}
// Example key: cached:@alice:phone@bob
else if (keyParts[0] == CACHED) {
metaData.isCached = true;
Expand Down Expand Up @@ -281,6 +346,21 @@ class PrivateKey extends AtKey {
}
}

/// Represents a local key
/// Local key are confined to the client(device)/server it is created.
/// The key does not sync between the local-secondary and the cloud-secondary.
class LocalKey extends AtKey {
LocalKey() {
isLocal = true;
super.metadata = Metadata();
}

@override
String toString() {
return 'local:$key${_dotNamespaceIfPresent()}$sharedBy';
}
}

class Metadata {
/// Represents the time in milliseconds beyond which the key expires
int? ttl;
Expand Down Expand Up @@ -437,6 +517,55 @@ class Metadata {
}
return metaData;
}

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Metadata &&
runtimeType == other.runtimeType &&
ttl == other.ttl &&
ttb == other.ttb &&
ttr == other.ttr &&
ccd == other.ccd &&
availableAt == other.availableAt &&
expiresAt == other.expiresAt &&
refreshAt == other.refreshAt &&
createdAt == other.createdAt &&
updatedAt == other.updatedAt &&
dataSignature == other.dataSignature &&
sharedKeyStatus == other.sharedKeyStatus &&
isPublic == other.isPublic &&
isHidden == other.isHidden &&
namespaceAware == other.namespaceAware &&
isBinary == other.isBinary &&
isEncrypted == other.isEncrypted &&
isCached == other.isCached &&
sharedKeyEnc == other.sharedKeyEnc &&
pubKeyCS == other.pubKeyCS &&
encoding == other.encoding;

@override
int get hashCode =>
ttl.hashCode ^
ttb.hashCode ^
ttr.hashCode ^
ccd.hashCode ^
availableAt.hashCode ^
expiresAt.hashCode ^
refreshAt.hashCode ^
createdAt.hashCode ^
updatedAt.hashCode ^
dataSignature.hashCode ^
sharedKeyStatus.hashCode ^
isPublic.hashCode ^
isHidden.hashCode ^
namespaceAware.hashCode ^
isBinary.hashCode ^
isEncrypted.hashCode ^
isCached.hashCode ^
sharedKeyEnc.hashCode ^
pubKeyCS.hashCode ^
encoding.hashCode;
}

class AtValue {
Expand All @@ -447,4 +576,11 @@ class AtValue {
String toString() {
return 'AtValue{value: $value, metadata: $metadata}';
}

@override
bool operator ==(Object other) =>
identical(this, other) || other is AtValue && runtimeType == other.runtimeType && value == other.value && metadata == other.metadata;

@override
int get hashCode => value.hashCode ^ metadata.hashCode;
}
8 changes: 8 additions & 0 deletions at_commons/lib/src/keystore/at_key_builder_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,11 @@ class PrivateKeyBuilder extends AbstractKeyBuilder {
_meta.isPublic = false;
}
}

/// Builder to build the local keys
class LocalKeyBuilder extends AbstractKeyBuilder {
LocalKeyBuilder() : super() {
_atKey = LocalKey();
_atKey.isLocal = true;
}
}
1 change: 1 addition & 0 deletions at_commons/lib/src/keystore/key_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ enum KeyType {
cachedPublicKey,
cachedSharedKey,
reservedKey,
localKey,
invalidKey
}

Expand Down
16 changes: 16 additions & 0 deletions at_commons/lib/src/utils/at_key_regex_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ abstract class Regexes {
'''(?<visibility>(cached:public:){1})((@$sharedWithFragment)?$entityFragment''';
static const String reservedKeyFragment =
'''(((@(?<sharedWith>($charsInAtSign|$allowedEmoji){1,55}))|public|privatekey):)?(?<atKey>$_charsInReservedKey)(@(?<owner>($charsInAtSign|$allowedEmoji){1,55}))?''';
static const String localKeyFragment =
'''(?<visibility>(local:){1})$entityFragment''';

String get publicKey;
String get privateKey;
Expand All @@ -42,6 +44,7 @@ abstract class Regexes {
String get cachedSharedKey;
String get cachedPublicKey;
String get reservedKey;
String get localKey;

static final Regexes _regexesWithMandatoryNamespace =
RegexesWithMandatoryNamespace();
Expand Down Expand Up @@ -71,6 +74,8 @@ class RegexesWithMandatoryNamespace implements Regexes {
'''${Regexes.cachedSharedKeyStartFragment}${Regexes.namespaceFragment}${Regexes.ownershipFragment}''';
static const String _cachedPublicKey =
'''${Regexes.cachedPublicKeyStartFragment}${Regexes.namespaceFragment}${Regexes.ownershipFragment}''';
static const String _localKey =
'''${Regexes.localKeyFragment}${Regexes.namespaceFragment}${Regexes.ownershipFragment}''';

@override
String get publicKey => _publicKey;
Expand All @@ -92,6 +97,9 @@ class RegexesWithMandatoryNamespace implements Regexes {

@override
String get reservedKey => Regexes.reservedKeyFragment;

@override
String get localKey => _localKey;
}

class RegexesNonMandatoryNamespace implements Regexes {
Expand All @@ -108,6 +116,8 @@ class RegexesNonMandatoryNamespace implements Regexes {
'''${Regexes.cachedSharedKeyStartFragment}${Regexes.ownershipFragment}''';
static const String _cachedPublicKey =
'''${Regexes.cachedPublicKeyStartFragment}${Regexes.ownershipFragment}''';
static const String _localkey =
'''${Regexes.localKeyFragment}${Regexes.ownershipFragment}''';

@override
String get publicKey => _publicKey;
Expand All @@ -129,6 +139,9 @@ class RegexesNonMandatoryNamespace implements Regexes {

@override
String get reservedKey => Regexes.reservedKeyFragment;

@override
String get localKey => _localkey;
}

class RegexUtil {
Expand Down Expand Up @@ -168,6 +181,9 @@ class RegexUtil {
if (matchAll(regexes.cachedSharedKey, key)) {
return KeyType.cachedSharedKey;
}
if (matchAll(regexes.localKey, key)) {
return KeyType.localKey;
}
return KeyType.invalidKey;
}

Expand Down
3 changes: 3 additions & 0 deletions at_commons/lib/src/validators/at_key_validation_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ class _AtKeyValidatorImpl extends AtKeyValidator {
case KeyType.reservedKey:
_regex = regexes.reservedKey;
break;
case KeyType.localKey:
_regex = regexes.localKey;
break;
case KeyType.invalidKey:
_regex = '';
break;
Expand Down
2 changes: 1 addition & 1 deletion at_commons/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: at_commons
description: A library of Dart and Flutter utility classes that are used across other components of the atPlatform.
version: 3.0.26
version: 3.0.27
repository: https://github.com/atsign-foundation/at_tools
homepage: https://atsign.dev

Expand Down
24 changes: 24 additions & 0 deletions at_commons/test/at_key_regex_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -411,4 +411,28 @@ void main() {
});
});
});

group('A group of test to validate local keys', () {
test('Test to validate local keys with enforcing namespaces', () {
var keyTypeList = [];
keyTypeList.add('local:phone.buzz@alice');
keyTypeList.add('local:pho_-n________e.b@alice');
keyTypeList.add('local:phone😀.buzz@alice💙');
for (var key in keyTypeList) {
var type = RegexUtil.keyType(key, true);
expect(type == KeyType.localKey, true);
}
});

test('Test to validate local keys without enforcing namespaces', () {
var keyTypeList = [];
keyTypeList.add('local:phone@alice');
keyTypeList.add('local:pho_-n________e@alice');
keyTypeList.add('local:phone😀@alice💙');
for (var key in keyTypeList) {
var type = RegexUtil.keyType(key, false);
expect(type == KeyType.localKey, true);
}
});
});
}
Loading

0 comments on commit 4b93b98

Please sign in to comment.