Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions app/lib/package/upload_signer_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:convert';

import 'package:_pub_shared/data/package_api.dart';
import 'package:_pub_shared/utils/http.dart';
import 'package:clock/clock.dart';
import 'package:gcloud/service_scope.dart' as ss;
import 'package:googleapis/iam/v1.dart' as iam;
Expand Down Expand Up @@ -105,20 +106,22 @@ abstract class UploadSignerService {
class _IamBasedUploadSigner extends UploadSignerService {
final String projectId;
final String email;
final iam.IamApi iamApi;
final http.Client authClient;

_IamBasedUploadSigner(this.projectId, this.email, http.Client client)
: iamApi = iam.IamApi(client);
_IamBasedUploadSigner(this.projectId, this.email, this.authClient);

@override
Future<SigningResult> sign(List<int> bytes) async {
final request = iam.SignBlobRequest()..bytesToSignAsBytes = bytes;
final name = 'projects/$projectId/serviceAccounts/$email';
final iam.SignBlobResponse response =
// TODO: figure out what new API we should use.
// ignore: deprecated_member_use
await iamApi.projects.serviceAccounts.signBlob(request, name);
return SigningResult(email, response.signatureAsBytes);
return await withRetryHttpClient(client: authClient, (client) async {
final iamApi = iam.IamApi(client);
final response =
// TODO: figure out what new API we should use.
// ignore: deprecated_member_use
await iamApi.projects.serviceAccounts.signBlob(request, name);
return SigningResult(email, response.signatureAsBytes);
});
}
}

Expand Down
7 changes: 4 additions & 3 deletions app/lib/publisher/domain_verifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:_pub_shared/utils/http.dart';
import 'package:clock/clock.dart';
import 'package:gcloud/service_scope.dart' as ss;
import 'package:googleapis/searchconsole/v1.dart' as wmx;
import 'package:googleapis_auth/googleapis_auth.dart' as auth;
import 'package:http/http.dart' as http;
import 'package:logging/logging.dart';
import 'package:retry/retry.dart' show retry;

import '../shared/exceptions.dart' show AuthorizationException;

Expand Down Expand Up @@ -47,8 +47,9 @@ class DomainVerifier {
);
try {
// Request list of sites/domains from the Search Console API.
final sites = await retry(
() => wmx.SearchConsoleApi(client).sites.list(),
final sites = await withRetryHttpClient(
client: client,
(client) => wmx.SearchConsoleApi(client).sites.list(),
maxAttempts: 3,
maxDelay: Duration(milliseconds: 500),
retryIf: (e) => e is! auth.AccessDeniedException,
Expand Down
48 changes: 23 additions & 25 deletions app/lib/service/email/email_sender.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:convert' show json;
import 'dart:io';

import 'package:_pub_shared/utils/http.dart';
import 'package:clock/clock.dart';
import 'package:gcloud/service_scope.dart' as ss;
import 'package:googleapis/iamcredentials/v1.dart' as iam_credentials;
Expand Down Expand Up @@ -264,12 +265,14 @@ class _GmailSmtpRelay extends EmailSenderBase {
/// [_serviceAccountEmail] configured for _domain-wide delegation_ following:
/// https://developers.google.com/identity/protocols/oauth2/service-account
Future<String> _createAccessToken(String sender) async {
final iam = iam_credentials.IAMCredentialsApi(_authClient);
final iat = clock.now().toUtc().millisecondsSinceEpoch ~/ 1000 - 20;
iam_credentials.SignJwtResponse jwtResponse;
try {
jwtResponse = await retry(
() => iam.projects.serviceAccounts.signJwt(
jwtResponse = await withRetryHttpClient(client: _authClient, (
client,
) async {
final iam = iam_credentials.IAMCredentialsApi(client);
return iam.projects.serviceAccounts.signJwt(
iam_credentials.SignJwtRequest()
..payload = json.encode({
'iss': _serviceAccountEmail,
Expand All @@ -280,8 +283,8 @@ class _GmailSmtpRelay extends EmailSenderBase {
'sub': sender,
}),
'projects/-/serviceAccounts/$_serviceAccountEmail',
),
);
);
});
} on Exception catch (e, st) {
_logger.severe(
'Signing JWT for sending email failed, '
Expand All @@ -294,29 +297,24 @@ class _GmailSmtpRelay extends EmailSenderBase {
);
}

final client = http.Client();
try {
return await withRetryHttpClient((client) async {
// Send a POST request with:
// Content-Type: application/x-www-form-urlencoded; charset=utf-8
return await retry(() async {
final r = await client.post(
_googleOauth2TokenUrl,
body: {
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion': jwtResponse.signedJwt,
},
final r = await client.post(
_googleOauth2TokenUrl,
body: {
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion': jwtResponse.signedJwt,
},
);
if (r.statusCode != 200) {
throw SmtpClientAuthenticationException(
'statusCode=${r.statusCode} from $_googleOauth2TokenUrl '
'while trying exchange JWT for access_token',
);
if (r.statusCode != 200) {
throw SmtpClientAuthenticationException(
'statusCode=${r.statusCode} from $_googleOauth2TokenUrl '
'while trying exchange JWT for access_token',
);
}
return json.decode(r.body)['access_token'] as String;
});
} finally {
client.close();
}
}
return json.decode(r.body)['access_token'] as String;
});
}
}

Expand Down
22 changes: 11 additions & 11 deletions app/lib/service/secret/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
import 'dart:async';
import 'dart:convert';

import 'package:_pub_shared/utils/http.dart';
import 'package:clock/clock.dart';
import 'package:gcloud/service_scope.dart' as ss;
import 'package:googleapis/secretmanager/v1.dart' as secretmanager;
import 'package:http/http.dart' as http;
import 'package:logging/logging.dart';
import 'package:pub_dev/shared/configuration.dart';
import 'package:pub_dev/shared/monitoring.dart';
import 'package:retry/retry.dart';

import 'models.dart';

Expand Down Expand Up @@ -72,17 +72,17 @@ final class GcpSecretBackend extends SecretBackend {

Future<String?> _lookup(String id) async {
try {
final api = secretmanager.SecretManagerApi(_client);
final secret = await retry(
() async => await api.projects.secrets.versions.access(
return await withRetryHttpClient(client: _client, (client) async {
final api = secretmanager.SecretManagerApi(client);
final secret = await api.projects.secrets.versions.access(
'projects/${activeConfiguration.projectId}/secrets/$id/versions/latest',
),
);
final data = secret.payload?.data;
if (data == null) {
return null;
}
return utf8.decode(base64.decode(data));
);
final data = secret.payload?.data;
if (data == null) {
return null;
}
return utf8.decode(base64.decode(data));
});
} catch (e, st) {
// Log the issue
_log.pubNoticeShout(
Expand Down
2 changes: 1 addition & 1 deletion app/lib/service/services.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Future<void> withServices(FutureOr<void> Function() fn) async {
)
: loggingEmailSender,
);
registerUploadSigner(await createUploadSigner(retryingAuthClient));
registerUploadSigner(await createUploadSigner(authClient));
registerSecretBackend(GcpSecretBackend(authClient));

// Configure a CloudCompute pool for later use in TaskBackend
Expand Down
2 changes: 2 additions & 0 deletions pkg/_pub_shared/lib/utils/http.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Future<K> httpGetWithRetry<K>(
Future<K> withRetryHttpClient<K>(
Future<K> Function(http.Client client) fn, {
int maxAttempts = 3,
Duration maxDelay = const Duration(seconds: 30),

/// The HTTP client to use.
///
Expand All @@ -109,6 +110,7 @@ Future<K> withRetryHttpClient<K>(
}
},
maxAttempts: maxAttempts,
maxDelay: maxDelay,
retryIf: (e) => _retryIf(e) || (retryIf != null && retryIf(e)),
);
}
Expand Down