blob: caf3b8bba48f584e637f47638333907a40a8d288 [file] [log] [blame]
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// 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 'dart:async';
import 'dart:io';
import '../authentication/credential.dart';
import '../command.dart';
import '../exceptions.dart';
import '../io.dart';
import '../log.dart' as log;
import '../source/hosted.dart';
/// Handles the `token add` pub command.
class TokenAddCommand extends PubCommand {
@override
String get name => 'add';
@override
String get description =>
'Add authentication tokens for a package repository.';
@override
String get invocation => 'pub token add';
@override
String get argumentsDescription => '[hosted-url]';
String? get envVar => argResults['env-var'];
TokenAddCommand() {
argParser.addOption('env-var',
help: 'Read the secret token from this environment variable when '
'making requests.');
}
@override
Future<void> runProtected() async {
if (argResults.rest.isEmpty) {
usageException(
'The [hosted-url] for a package repository must be given.');
} else if (argResults.rest.length > 1) {
usageException('Takes only a single argument.');
}
final rawHostedUrl = argResults.rest.first;
try {
var hostedUrl = validateAndNormalizeHostedUrl(rawHostedUrl);
if (!hostedUrl.isScheme('HTTPS')) {
throw FormatException('url must be https://, '
'insecure repositories cannot use authentication.');
}
if (envVar == null) {
await _addTokenFromStdin(hostedUrl);
} else {
await _addEnvVarToken(hostedUrl, envVar!);
}
} on FormatException catch (e) {
usageException('Invalid [hosted-url]: "$rawHostedUrl"\n'
'${e.message}');
}
}
Future<void> _addTokenFromStdin(Uri hostedUrl) async {
final token = await stdinPrompt('Enter secret token:', echoMode: false);
if (token.isEmpty) {
usageException('Token is not provided.');
}
tokenStore.addCredential(Credential.token(hostedUrl, token));
log.message(
'Requests to "$hostedUrl" will now be authenticated using the secret '
'token.',
);
}
Future<void> _addEnvVarToken(Uri hostedUrl, String envVar) async {
if (envVar.isEmpty) {
usageException('Cannot use the empty string as --env-var');
}
// Environment variable names on Windows [1] and UNIX [2] cannot contain
// equal signs.
// [1] https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables
// [2] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
if (envVar.contains('=')) {
throw DataException(
'Environment variable name --env-var="$envVar" cannot contain "=", the '
'equals sign is not allowed in environment variable names.',
);
}
// Help the user if they typed something that is unlikely to be correct.
// This could happen if you include $, whitespace, quotes or accidentally
// dereference the environment variable instead.
if (!RegExp(r'^[A-Z_][A-Z0-9_]*$').hasMatch(envVar)) {
log.warning(
'The environment variable name --env-var="$envVar" does not use '
'uppercase characters A-Z, 0-9 and underscore. This is unusual for '
'environment variable names.\n'
'Check that you meant to use the environment variable name: "$envVar".',
);
}
tokenStore.addCredential(Credential.env(hostedUrl, envVar));
log.message(
'Requests to "$hostedUrl" will now be authenticated using the secret '
'token stored in the environment variable "$envVar".',
);
if (!Platform.environment.containsKey(envVar)) {
// If environment variable doesn't exist when
// pub token add <hosted-url> --env-var <ENV_VAR> is called, we should
// print a warning.
log.warning('Environment variable "$envVar" is not defined.');
}
}
}