Skip to content
Draft
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
4 changes: 4 additions & 0 deletions src/Selector/Selector.php
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,10 @@ public function selectOrganization(InputInterface $input, string $filterByLink =
}
}

if ($this->api->isUsingClientCredentials()) {
throw new \InvalidArgumentException('An organization name or ID (--org) is required when client credentials are in use.');
}

$userId = $this->api->getMyUserId();
$organizations = $this->api->getClient()->listOrganizationsWithMember($userId);

Expand Down
45 changes: 39 additions & 6 deletions src/Service/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ private function getConnectorOptions(): array
$connectorOptions['verify'] = $this->config->getBool('api.skip_ssl') ? false : $this->caBundlePath();

$connectorOptions['debug'] = false;
$connectorOptions['client_id'] = $this->config->get('api.oauth2_client_id');
$connectorOptions['client_id'] = $this->config->getStr('api.oauth2_client_id');
$connectorOptions['user_agent'] = $this->config->getUserAgent();
$connectorOptions['timeout'] = $this->config->getInt('api.default_timeout');

Expand All @@ -292,6 +292,13 @@ private function getConnectorOptions(): array
$connectorOptions['api_token_type'] = 'access';
}

if ($this->config->has('api.oauth2_client_secret')) {
$connectorOptions['client_secret'] = $this->config->getStr('api.oauth2_client_secret');
}
if ($this->config->has('api.oauth2_scopes')) {
$connectorOptions['scopes'] = (array) $this->config->get('api.oauth2_scopes');
}

$connectorOptions['proxy'] = $this->guzzleProxyConfig();

$connectorOptions['token_url'] = $this->config->get('api.oauth2_token_url');
Expand Down Expand Up @@ -510,11 +517,17 @@ public function getClient(bool $autoLogin = true, bool $reset = false): Platform

$sessionId = $this->config->getSessionId();

// Override the session ID if an API token is set.
// This ensures file storage from other credentials will not be
// reused.
if (!empty($options['api_token'])) {
$sessionId = 'api-token-' . \substr(\hash('sha256', (string) $options['api_token']), 0, 32);
// Override the session ID if an API token or client credentials
// are set. This ensures file storage from other credentials will
// not be reused.
if (!empty($options['api_token']) || !empty($options['client_secret'])) {
$credsKeys = [];
foreach (['api_token', 'client_id', 'client_secret', 'scopes'] as $key) {
if (array_key_exists($key, $options)) {
$credsKeys[] = is_array($options[$key]) ? implode(' ', $options[$key]) : $options[$key];
}
}
$sessionId = 'c-' . \hash('sha256', implode(':', $credsKeys));
}

// Set up a session to store OAuth2 tokens.
Expand Down Expand Up @@ -628,6 +641,11 @@ private function matchesVendorFilter(string|array|null $filters, BasicProjectInf
return empty($filters) || in_array($project->vendor, (array) $filters);
}

public function isUsingClientCredentials(): bool
{
return $this->config->has('api.oauth2_client_secret') && $this->getClient()->getMyUserId() === false;
}

/**
* Returns the project list for the current user.
*
Expand All @@ -637,6 +655,10 @@ private function matchesVendorFilter(string|array|null $filters, BasicProjectInf
*/
public function getMyProjects(?bool $refresh = null): array
{
if ($this->isUsingClientCredentials()) {
return [];
}

$new = $this->config->getBool('api.centralized_permissions') && $this->config->getBool('api.organizations');
/** @var string[]|string|null $vendorFilter */
$vendorFilter = $this->config->getWithDefault('api.vendor_filter', null);
Expand Down Expand Up @@ -1672,6 +1694,17 @@ public function getCodeSourceIntegration(Project $project): ?Integration
*/
public function showSessionInfo(bool $logout = false, bool $newline = true): void
{
if ($this->isUsingClientCredentials()) {
if ($newline) {
$this->stdErr->writeln('');
}
$this->stdErr->writeln(\sprintf(
'Client credentials are configured (client ID: <info>%s</info>)',
$this->config->getStr('api.oauth2_client_id'),
));
return;
}

$sessionId = $this->config->getSessionId();
if ($sessionId !== 'default' || count($this->listSessionIds()) > 1) {
if ($newline) {
Expand Down
5 changes: 5 additions & 0 deletions src/Service/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,8 @@ private function applyEnvironmentOverrides(): void
'AUTH_URL' => 'api.auth_url',
'OAUTH2_AUTH_URL' => 'api.oauth2_auth_url',
'OAUTH2_CLIENT_ID' => 'api.oauth2_client_id',
'OAUTH2_CLIENT_SECRET' => 'api.oauth2_client_secret',
'OAUTH2_SCOPE' => 'api.oauth2_scopes',
'OAUTH2_TOKEN_URL' => 'api.oauth2_token_url',
'OAUTH2_REVOKE_URL' => 'api.oauth2_revoke_url',
'CERTIFIER_URL' => 'api.certifier_url',
Expand Down Expand Up @@ -672,6 +674,9 @@ private function applyDynamicDefaults(): void
if (!isset($this->config['api']['oauth2_client_id'])) {
$this->config['api']['oauth2_client_id'] = $this->getStr('application.slug');
}
if (isset($this->config['api']['oauth2_scopes']) && is_string($this->config['api']['oauth2_scopes'])) {
$this->config['api']['oauth2_scopes'] = explode(' ', $this->config['api']['oauth2_scopes']);
}
if (!isset($this->config['detection']['console_domain']) && isset($this->config['service']['console_url'])) {
$consoleDomain = parse_url((string) $this->config['service']['console_url'], PHP_URL_HOST);
if ($consoleDomain !== false) {
Expand Down
Loading