Skip to content

Commit

Permalink
Resolve AwsClientCredentials if not provided (#166)
Browse files Browse the repository at this point in the history
* Resolve AwsClientCredentials if not provided

* Fix bug in resolving, roll own credentials parser

Co-authored-by: Jonathan Böcker <jonathan.bocker1@ingka.com>
  • Loading branch information
Schwusch and Schwusch committed Apr 24, 2020
1 parent 66531c6 commit 628e921
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 10 deletions.
2 changes: 1 addition & 1 deletion generator/lib/builders/protocols/json_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class JsonServiceBuilder extends ServiceBuilder {
@override
String constructor() => '''
final _s.JsonProtocol _protocol;
${api.metadata.className}({@_s.required String region, @_s.required _s.AwsClientCredentials credentials, _s.Client client, String endpointUrl,})
${api.metadata.className}({@_s.required String region, _s.AwsClientCredentials credentials, _s.Client client, String endpointUrl,})
: _protocol = _s.JsonProtocol(client: client, service: \'${api.metadata.endpointPrefix}\', region: region, credentials: credentials, endpointUrl: endpointUrl,);
''';

Expand Down
2 changes: 1 addition & 1 deletion generator/lib/builders/protocols/query_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class QueryServiceBuilder extends ServiceBuilder {
String constructor() => '''
final _s.QueryProtocol _protocol;
${api.metadata.className}({@_s.required String region, @_s.required _s.AwsClientCredentials credentials, _s.Client client,})
${api.metadata.className}({@_s.required String region, _s.AwsClientCredentials credentials, _s.Client client,})
: _protocol = _s.QueryProtocol(client: client, service: \'${api.metadata.endpointPrefix}\', region: region, credentials: credentials,);
''';

Expand Down
2 changes: 1 addition & 1 deletion generator/lib/builders/protocols/rest_json_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class RestJsonServiceBuilder extends ServiceBuilder {
@override
String constructor() => '''
final _s.RestJsonProtocol _protocol;
${api.metadata.className}({@_s.required String region, @_s.required _s.AwsClientCredentials credentials, _s.Client client, String endpointUrl,})
${api.metadata.className}({@_s.required String region, _s.AwsClientCredentials credentials, _s.Client client, String endpointUrl,})
: _protocol = _s.RestJsonProtocol(client: client, service: \'${api.metadata.endpointPrefix}\', region: region, credentials: credentials, endpointUrl: endpointUrl,);
''';

Expand Down
2 changes: 1 addition & 1 deletion generator/lib/builders/protocols/rest_xml_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class RestXmlServiceBuilder extends ServiceBuilder {
String constructor() {
return '''
final _s.RestXmlProtocol _protocol;
${api.metadata.className}({@_s.required String region, @_s.required _s.AwsClientCredentials credentials, _s.Client client, String endpointUrl,})
${api.metadata.className}({@_s.required String region, _s.AwsClientCredentials credentials, _s.Client client, String endpointUrl,})
: _protocol = _s.RestXmlProtocol(client: client, service: \'${api.metadata.endpointPrefix}\', region: region, credentials: credentials, endpointUrl: endpointUrl,);
''';
}
Expand Down
5 changes: 5 additions & 0 deletions shared_aws_api/lib/src/credentials.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import 'package:meta/meta.dart';

import 'credentials/credentials_io.dart'
if (dart.library.html) 'credentials/credentials_html.dart';

/// AWS credentials.
class AwsClientCredentials {
/// AWS access key
Expand All @@ -24,4 +27,6 @@ class AwsClientCredentials {
assert(accessKey != null);
assert(secretKey != null);
}

static AwsClientCredentials resolve() => CredentialsUtil.resolve();
}
5 changes: 5 additions & 0 deletions shared_aws_api/lib/src/credentials/credentials_html.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import 'package:shared_aws_api/src/credentials.dart';

class CredentialsUtil {
static AwsClientCredentials resolve() => null;
}
74 changes: 74 additions & 0 deletions shared_aws_api/lib/src/credentials/credentials_io.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import 'dart:io';

import 'package:shared_aws_api/src/credentials.dart';
import 'package:shared_aws_api/src/credentials/ini_config.dart';

class CredentialsUtil {
static AwsClientCredentials resolve() {
return fromEnvironment ?? fromProfileFile;
}

static AwsClientCredentials get fromEnvironment {
final environment = Platform.environment;
final accessKey = environment['AWS_ACCESS_KEY_ID'];
final secretKey = environment['AWS_SECRET_ACCESS_KEY'];
final sessionToken = environment['AWS_SESSION_TOKEN'];

if (accessKey == null || secretKey == null) {
return null;
}

return AwsClientCredentials(
accessKey: accessKey,
secretKey: secretKey,
sessionToken: sessionToken,
);
}

static String get userHome =>
Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];

static AwsClientCredentials get fromProfileFile {
final environment = Platform.environment;

final credentialsFile = File(
environment['AWS_SHARED_CREDENTIALS_FILE'] ??
'$userHome/.aws/credentials',
);

if (!credentialsFile.existsSync()) {
return null;
}

final credentialsString = credentialsFile.readAsStringSync();
Config config;
try {
config = Config.fromString(credentialsString);
} catch (e) {
print('Parsing ${credentialsFile.path} failed: ${e.toString()}');
return null;
}

final profile = environment['AWS_PROFILE'] ?? 'default';

if (!config.hasSection(profile)) {
print('${credentialsFile.path} does not contain [$profile] profile');
return null;
}

final secretKey = config.get(profile, 'aws_secret_access_key') ??
config.get(profile, 'aws_access_secret_key');
if (secretKey == null) {
print('profile [$profile] does not contain "aws_secret_access_key"');
return null;
}

final accessKey = config.get(profile, 'aws_access_key_id');
if (accessKey == null) {
print('profile [$profile] does not contain "aws_access_key_id"');
return null;
}

return AwsClientCredentials(accessKey: accessKey, secretKey: secretKey);
}
}
126 changes: 126 additions & 0 deletions shared_aws_api/lib/src/credentials/ini_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import 'dart:convert';

class Config {
final Map<String, Map<String, String>> _sections =
<String, Map<String, String>>{};
List<String> _strings;

static final RegExp _blankLinePattern = RegExp(r'^\s*$');
static final RegExp _commentPattern = RegExp(r'^\s*[;#]');
static final RegExp _lineContinuationPattern = RegExp(r'^\s+');
static final RegExp _sectionPattern = RegExp(r'^\s*\[(.*\S.*)]\s*$');
static final RegExp _entryPattern = RegExp(r'^([^=]+)=(.*?)$');

static Iterable<String> _removeBlankLines(Iterable<String> source) =>
source.where((String line) => !_blankLinePattern.hasMatch(line));

static Iterable<String> _removeComments(Iterable<String> source) =>
source.where((String line) => !_commentPattern.hasMatch(line));

static List<String> _joinLongHeaderFields(Iterable<String> source) {
final result = <String>[];
var line = '';

for (final current in source) {
if (_lineContinuationPattern.hasMatch(current)) {
line += current.replaceFirst(_lineContinuationPattern, '');
} else {
if (line != '') {
result.add(line);
}
line = current;
}
}
if (line != '') {
result.add(line);
}

return result;
}

static Config fromString(String string) =>
fromStrings(LineSplitter().convert(string));

static Config fromStrings(List<String> strings) {
final config = Config()
.._strings =
_joinLongHeaderFields(_removeComments(_removeBlankLines(strings)));
var section = 'default';

for (final current in config._strings) {
final isSection = _sectionPattern.firstMatch(current);
if (isSection != null) {
section = isSection[1].trim();
config.addSection(section);
} else {
final isEntry = _entryPattern.firstMatch(current);
if (isEntry != null) {
config.set(section, isEntry[1].trim(), isEntry[2].trim());
} else {
throw Exception('Unrecognized line: "$current"');
}
}
}
return config;
}

Iterable<String> sections() => _sections.keys;

void addSection(String name) {
if (_sections.containsKey(name)) {
throw Exception('DuplicateSectionError');
}
_sections[name] = <String, String>{};
}

bool hasSection(String name) => _sections.containsKey(name);

Iterable<String> options(String name) => _getSection(name)?.keys;

bool hasOption(String name, String option) =>
(_getSection(name) ?? {})?.containsKey(option) ?? false;

String get(String name, String option) => (_getSection(name) ?? {})[option];

List<List<String>> items(String name) {
final s = _getSection(name);
return s != null
? s.keys.map((String key) => [key, s[key]]).toList()
: null;
}

void set(String name, String option, String value) {
final s = _getSection(name);
if (s == null) {
throw Exception('NoSectionError');
}
s[option] = value;
}

bool removeOption(String section, String option) {
final s = _getSection(section);
if (s != null) {
if (s.containsKey(option)) {
s.remove(option);
return true;
}
return false;
}
throw Exception('NoSectionError');
}

bool removeSection(String section) {
if (_sections.containsKey(section)) {
_sections.remove(section);
return true;
}
return false;
}

Map<String, String> _getSection(String section) {
if (_sections.containsKey(section)) {
return _sections[section];
}
return null;
}
}
2 changes: 2 additions & 0 deletions shared_aws_api/lib/src/protocol/json.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class JsonProtocol {
endpointUrl ??= 'https://$service.$region.amazonaws.com';
service ??= extractService(Uri.parse(endpointUrl));
region ??= extractRegion(Uri.parse(endpointUrl));
credentials ??= AwsClientCredentials.resolve();
ArgumentError.checkNotNull(credentials, 'credentials');
return JsonProtocol._(client, service, region, endpointUrl, credentials);
}

Expand Down
2 changes: 2 additions & 0 deletions shared_aws_api/lib/src/protocol/query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class QueryProtocol {
endpointUrl ??= 'https://$service.$region.amazonaws.com';
service ??= extractService(Uri.parse(endpointUrl));
region ??= extractRegion(Uri.parse(endpointUrl));
credentials ??= AwsClientCredentials.resolve();
ArgumentError.checkNotNull(credentials, 'credentials');
return QueryProtocol._(client, service, region, endpointUrl, credentials);
}

Expand Down
2 changes: 2 additions & 0 deletions shared_aws_api/lib/src/protocol/rest-json.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class RestJsonProtocol {
endpointUrl ??= 'https://$service.$region.amazonaws.com';
service ??= extractService(Uri.parse(endpointUrl));
region ??= extractRegion(Uri.parse(endpointUrl));
credentials ??= AwsClientCredentials.resolve();
ArgumentError.checkNotNull(credentials, 'credentials');
return RestJsonProtocol._(
client, service, region, endpointUrl, credentials);
}
Expand Down
2 changes: 2 additions & 0 deletions shared_aws_api/lib/src/protocol/rest-xml.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class RestXmlProtocol {
endpointUrl ??= 'https://$service.$region.amazonaws.com';
service ??= extractService(Uri.parse(endpointUrl));
region ??= extractRegion(Uri.parse(endpointUrl));
credentials ??= AwsClientCredentials.resolve();
ArgumentError.checkNotNull(credentials, 'credentials');
return RestXmlProtocol._(client, service, region, endpointUrl, credentials);
}

Expand Down
12 changes: 6 additions & 6 deletions shared_aws_api/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ environment:
sdk: '>=2.7.0 <3.0.0'

dependencies:
crypto: ^2.1.3
http: ^0.12.0+2
json_annotation: ^3.0.0
crypto: ^2.1.4
http: ^0.12.0+4
json_annotation: ^3.0.1
meta: ^1.1.8
xml: ^3.7.0
xml: ^4.1.0
intl: ^0.16.1

dev_dependencies:
pedantic: ^1.8.0+1
test: ^1.9.4
pedantic: ^1.9.0
test: ^1.14.2

0 comments on commit 628e921

Please sign in to comment.