Skip to content

Commit

Permalink
Rename "Application Password" to "QIT Token" and other improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Luc45 committed Jan 26, 2024
1 parent 0558c5b commit f503677
Show file tree
Hide file tree
Showing 12 changed files with 101 additions and 84 deletions.
Binary file modified qit
Binary file not shown.
23 changes: 15 additions & 8 deletions src/src/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@ public function __construct( Environment $environment ) {
/**
* @return string|null base64 encoded string of user:application_password, or null if not defined.
*/
public function get_app_pass() {
$user = $this->environment->get_cache()->get( 'user' );
$application_password = $this->environment->get_cache()->get( 'application_password' );
public function get_partner_auth() {
// Migrate "application_password" to "qit_token" if it exists.
if ( ! empty( $this->environment->get_cache()->get( 'application_password' ) ) ) {
$this->environment->get_cache()->set( 'qit_token', $this->environment->get_cache()->get( 'application_password' ), - 1 );
$this->environment->get_cache()->delete( 'application_password' );
}


$user = $this->environment->get_cache()->get( 'user' );
$qit_token = $this->environment->get_cache()->get( 'qit_token' );

if ( ! empty( $user ) && ! empty( $application_password ) ) {
return base64_encode( sprintf( '%s:%s', $user, $application_password ) );
if ( ! empty( $user ) && ! empty( $qit_token ) ) {
return base64_encode( sprintf( '%s:%s', $user, $qit_token ) );
}

return null;
Expand All @@ -33,13 +40,13 @@ public function get_manager_secret() {

/**
* @param string $user
* @param string $app_pass
* @param string $qit_token
*
* @return void
*/
public function set_auth_app_pass( $user, $app_pass ): void {
public function set_partner_auth( $user, $qit_token ): void {
$this->environment->get_cache()->set( 'user', $user, - 1 );
$this->environment->get_cache()->set( 'application_password', $app_pass, - 1 );
$this->environment->get_cache()->set( 'qit_token', $qit_token, - 1 );
}

/**
Expand Down
41 changes: 5 additions & 36 deletions src/src/Commands/OnboardingCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,7 @@ protected function configure() {

protected function execute( InputInterface $input, OutputInterface $output ): int {
$io = new SymfonyStyle( $input, $output );
$io->title( 'Woo Quality Insights Toolkit CLI' );

$io->section( <<<SECTION
Documentation: https://woocommerce.github.io/qit-documentation/
Running Tests: https://woocommerce.github.io/qit-documentation/#/cli/running-tests
SECTION
);

$io->writeln( '<comment>Examples:</comment>' );

$io->writeln( "\n<info>Running a Security Test:</info>" );
$io->writeln( './qit run:security my-extension-slug' );

$io->writeln( "\n<info>Running a Security Test against a development build:</info>" );
$io->writeln( './qit run:security my-extension-slug --zip=my-extension-slug.zip' );

$io->writeln( "\n<info>Running a WooCommerce Core E2E test with configurable options against a dev build:</info>" );
$io->writeln(<<<COMMAND
./qit run:e2e my-extension-slug \
--woocommerce_version=7.6.0-rc.2 \
--php_version=8.2 \
--wordpress_version=6.2 \
--optional_features=hpos \
--additional_woo_plugins=woocommerce-shipping \
--additional_wordpress_plugins=hello-dolly \
--zip=my-extension-slug.zip
COMMAND
);

$io->writeln( sprintf( '<comment>%s</comment>', str_repeat( '-', ( new Terminal() )->getWidth() ) ) );
$io->title( 'Quality Insights Toolkit (QIT)' );

$output->write( "\n<comment>Connecting to QIT servers... </comment>" );

Expand All @@ -70,26 +40,25 @@ protected function execute( InputInterface $input, OutputInterface $output ): in
$question_helper = $this->getHelper( 'question' );

if ( $is_proxied ) {
$io->section( 'Onboarding as Automattician (Connected to Proxy)' );
$io->section( 'Authenticate as Automattician (Connected to Proxy)' );

$io->writeln( $proxied_instructions . "\n" );

$user = new Question( 'Please enter the user: ' );
$user = $question_helper->ask( $input, $output, $user );

$app_pass = new Question( 'Please enter the application password: ' );
$app_pass = new Question( 'Please enter the QIT Token: ' );
$app_pass->setHidden( true );
$app_pass = $question_helper->ask( $input, $output, $app_pass );

$command = $this->getApplication()->find( AddPartner::getDefaultName() );

return $command->run( new ArrayInput( [
'--user' => $user,
'--application_password' => $app_pass,
'--qit_token' => $app_pass,
] ), $output );
} else {
$io->section( 'Onboarding as Partner of the Woo Marketplace' );
$io->info( 'If you are an Automattic employee, please connect to the proxy and run the CLI again.' );
$io->section( 'Authenticate as Woo.com Partner Developer' );

$command = $this->getApplication()->find( AddPartner::getDefaultName() );

Expand Down
99 changes: 70 additions & 29 deletions src/src/Commands/Partner/AddPartner.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
use function QIT_CLI\get_manager_url;
use function QIT_CLI\get_wccom_url;
use function QIT_CLI\validate_authentication;
Expand All @@ -36,9 +37,10 @@ public function __construct( Environment $environment, Auth $auth, WooExtensions
protected function configure() {
$this
->setDescription( 'Configure a new WCCOM Marketplace Partner that the QIT CLI can connect to.' )
->setHelp( sprintf( 'Configure the QIT CLI to be able to interact with %s on behalf of a given partner.', get_wccom_url() ) )
->addOption( 'user', 'u', InputOption::VALUE_OPTIONAL, '(Optional) WCCOM Marketplace user with "edit" permission to the extensions that you want to test.' )
->addOption( 'application_password', 'p', InputOption::VALUE_OPTIONAL, '(Optional) Partner application password.' );
->setHelp( sprintf( "Configure the QIT CLI to be able to interact with %s on behalf of a given partner.\nAuthenticating documentation: https://woocommerce.github.io/qit-documentation/#/authenticating", get_wccom_url() ) )
->addOption( 'user', 'u', InputOption::VALUE_OPTIONAL, '(Optional) Woo.com Partner Developer username.' )
->addOption( 'qit_token', 't', InputOption::VALUE_OPTIONAL, '(Optional) Woo.com Partner Developer QIT Token.' )
->addOption( 'application_password', 'p', InputOption::VALUE_OPTIONAL, '(DEPRECATED) This has been renamed to "QIT Token" and will be removed. A regular application password will not work.' );
}

protected function execute( InputInterface $input, OutputInterface $output ): int {
Expand All @@ -50,68 +52,75 @@ protected function execute( InputInterface $input, OutputInterface $output ): in
$user = $this->getHelper( 'question' )->ask( $input, $output, $question );
}

// Application Password.
$qit_token = null;

// (Deprecated) Application password.
if ( ! empty( $input->getOption( 'application_password' ) ) ) {
$application_password = $input->getOption( 'application_password' );
} else {
$output->writeln( '<comment>"Application Password" has been renamed to "QIT Token" and will be removed in the future.</comment>' );
$qit_token = $input->getOption( 'application_password' );
}

// Or QIT Token.
if ( ! empty( $input->getOption( 'qit_token' ) ) ) {
$qit_token = $input->getOption( 'qit_token' );
}

// Ask for QIT Token.
if ( is_null( $qit_token ) ) {
$authorize_url = $this->get_authorize_url( $output );
$wccom_url = get_wccom_url();

$output->writeln( <<<TEXT
To generate an Application Password, please follow these steps:
If you don't have a QIT Token yet, please follow these steps to generate one.
1. Go to $wccom_url/my-account
2. Login with a Partner account that has permissions to manage the extensions you want to test.
3. Go to $authorize_url
4. Authorize the connection, copy the Application Password that will be generated and paste it here.
<info>If you are redirected back to "my-account" on step 3, it's because you are not logged in with a Partner account.
Contact someone in your organization that has access to the Partner account in WCCOM to generate the Application Password for you.</info>
1. Login as "$user" in $wccom_url
2. Go to $authorize_url
3. Authorize the connection, copy the generated QIT Token and paste it here.
Note: The input is protected, so you won't be able to see it on your terminal.
TEXT
);

$question = new Question( "<question>Please paste the Application Password here and press 'Enter'</question> " );
$question = new Question( "<question>Please paste the QIT Token here and press 'Enter'</question> " );
$question->setHidden( true );
$question->setHiddenFallback( false );
$question->setMaxAttempts( 1 );

$question->setValidator( function ( $application_password ) {
if ( empty( $application_password ) ) {
throw new \RuntimeException( 'Invalid Application Password.' );
$question->setValidator( function ( $qit_token ) {
if ( empty( $qit_token ) ) {
throw new \RuntimeException( 'Invalid QIT Token. Questions? https://woocommerce.github.io/qit-documentation/#/authenticating' );
}

if ( ! preg_match( '#^[a-z0-9 ]+$#i', $application_password ) ) {
throw new \RuntimeException( 'An application password should consist of alpha-numeric characters and spaces.' );
if ( ! preg_match( '#^[a-z0-9 ]+$#i', $qit_token ) ) {
throw new \RuntimeException( 'A QIT Token should consist of alpha-numeric characters and spaces. Questions? https://woocommerce.github.io/qit-documentation/#/authenticating' );
}

return $application_password;
return $qit_token;
} );

$question->setNormalizer( function ( $application_password ) {
return str_replace( ' ', '', $application_password );
$question->setNormalizer( function ( $qit_token ) {
return str_replace( ' ', '', $qit_token );
} );

$application_password = $this->getHelper( 'question' )->ask( $input, $output, $question );
$qit_token = $this->getHelper( 'question' )->ask( $input, $output, $question );
}

$manager_url = get_manager_url();

$user = strtolower( $user );

if ( ! filter_var( $user, FILTER_VALIDATE_EMAIL ) && ! preg_match( '#^[a-z0-9_-]{1,70}$#i', $user ) ) {
throw new \InvalidArgumentException( 'The username must be either a valid e-mail, or contain only letters, numbers, underscores or dashes.' );
throw new \InvalidArgumentException( 'The username must be either a valid e-mail, or contain only letters, numbers, underscores or dashes. Questions? https://woocommerce.github.io/qit-documentation/#/authenticating' );
}

// Remove any non-alphanumeric characters from the username.
$user_environment = preg_replace( '#[^a-z0-9_-]#i', '', $user );

// Validate credentials.
$output->writeln( sprintf( 'Validating your application password with %s...', get_wccom_url() ) );
validate_authentication( $user, $application_password );
$output->writeln( sprintf( 'Validating your QIT Token with %s...', get_wccom_url() ) );
validate_authentication( $user, $qit_token );
$output->writeln( '<fg=green>Validated successfully.</>' );

$easter_egg = <<<TEXT
Expand All @@ -132,14 +141,46 @@ protected function execute( InputInterface $input, OutputInterface $output ): in

$this->environment->create_partner( $user_environment );

$this->auth->set_auth_app_pass( $user, $application_password );
$this->auth->set_partner_auth( $user, $qit_token );
$this->environment->get_cache()->set( 'manager_url', $manager_url, - 1 );
$this->woo_extensions_list->fetch_woo_extensions_available();

$output->writeln( sprintf( "Cache file written to: %s. Keep this file safe, as it contains your Application Password.\n", $this->environment->get_cache()->get_cache_file_path() ) );
$output->writeln( sprintf( "Cache file written to: %s. Keep this file safe, as it contains your QIT Token.\n", $this->environment->get_cache()->get_cache_file_path() ) );
$output->writeln( "Treat this file as you would treat your SSH keys. For more tips on hardening security, check the README of the QIT CLI.\n" );
$output->writeln( '<fg=green>Initialization complete! You can now start using the QIT CLI!</>' );

$io = new SymfonyStyle( $input, $output );

$io->section( <<<SECTION
Getting Started:
Documentation: https://woocommerce.github.io/qit-documentation/
Running Tests: https://woocommerce.github.io/qit-documentation/#/cli/running-tests
SECTION
);

$io->writeln( '<comment>Examples:</comment>' );

$io->writeln( "\n<info>Running a Security Test:</info>" );
$io->writeln( './qit run:security my-extension-slug' );

$io->writeln( "\n<info>Running a Security Test against a development build:</info>" );
$io->writeln( './qit run:security my-extension-slug --zip=plugin.zip' );

$io->writeln( "\n<info>Running a WooCommerce Core E2E test with configurable options using a development build:</info>" );
$io->writeln(<<<COMMAND
./qit run:woo-e2e my-extension-slug \
--woocommerce_version=rc \
--wordpress_version=rc \
--php_version=8.2 \
--optional_features=hpos \
--additional_woo_plugins=woocommerce-shipping \
--additional_wordpress_plugins=hello-dolly \
--zip
COMMAND
);

return Command::SUCCESS;
}

Expand Down
4 changes: 2 additions & 2 deletions src/src/RequestBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ public function request(): string {
$curl_parameters[ CURLOPT_PROXY ] = Config::get_proxy_url();
$curl_parameters[ CURLOPT_PROXYTYPE ] = CURLPROXY_SOCKS5;
}
} elseif ( ! is_null( App::make( Auth::class )->get_app_pass() ) ) {
$this->post_body['partner_app_pass'] = App::make( Auth::class )->get_app_pass();
} elseif ( ! is_null( App::make( Auth::class )->get_partner_auth() ) ) {
$this->post_body['partner_app_pass'] = App::make( Auth::class )->get_partner_auth();
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/src/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ function is_windows(): bool {
return defined( 'PHP_WINDOWS_VERSION_BUILD' );
}

function validate_authentication( string $username, string $application_password ): void {
function validate_authentication( string $username, string $qit_token ): void {
$is_ci = getenv( 'CI' );

try {
( new RequestBuilder( get_manager_url() . '/wp-json/cd/v1/cli/partner-auth' ) )
->with_method( 'POST' )
->with_retry( $is_ci ? 8 : 2 ) // Retry many times due to parallel test runs in CI which might cause 429.
->with_post_body( [
'app_pass' => base64_encode( sprintf( '%s:%s', $username, $application_password ) ),
'app_pass' => base64_encode( sprintf( '%s:%s', $username, $qit_token ) ),
] )
->request();
} catch ( \Exception $e ) {
throw new \Exception( sprintf( 'Could not authenticate to %s using the provided username and application password.', get_wccom_url() ) );
throw new \Exception( sprintf( 'Could not authenticate to %s using the provided username and QIT Token. Questions? https://woocommerce.github.io/qit-documentation/#/authenticating', get_wccom_url() ) );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"expire": -1,
"value": "foo_user"
},
"application_password": {
"qit_token": {
"expire": -1,
"value": "foo_pass"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"expire": -1,
"value": "[email protected]"
},
"application_password": {
"qit_token": {
"expire": -1,
"value": "foo_pass"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"expire": -1,
"value": "bar_user"
},
"application_password": {
"qit_token": {
"expire": -1,
"value": "bar_pass"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"expire": -1,
"value": "foo_user"
},
"application_password": {
"qit_token": {
"expire": -1,
"value": "foo_pass"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"expire": -1,
"value": "staging_user"
},
"application_password": {
"qit_token": {
"expire": -1,
"value": "staging_pass"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"expire": -1,
"value": "local_user"
},
"application_password": {
"qit_token": {
"expire": -1,
"value": "local_pass"
},
Expand Down

0 comments on commit f503677

Please sign in to comment.