Skip to content

Commit 47e2b2d

Browse files
authored
CM-27690 - Configure API URL and APP URL using "cycode configure" (#167)
1 parent 5e10e07 commit 47e2b2d

File tree

9 files changed

+374
-204
lines changed

9 files changed

+374
-204
lines changed

cycode/cli/commands/configure/__init__.py

Whitespace-only changes.
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
from typing import Optional
2+
3+
import click
4+
5+
from cycode.cli import config, consts
6+
from cycode.cli.user_settings.configuration_manager import ConfigurationManager
7+
from cycode.cli.user_settings.credentials_manager import CredentialsManager
8+
from cycode.cli.utils.string_utils import obfuscate_text
9+
10+
_URLS_UPDATED_SUCCESSFULLY_MESSAGE = 'Successfully configured Cycode URLs! Saved to: {filename}'
11+
_URLS_ARE_SET_IN_ENVIRONMENT_VARIABLES_MESSAGE = (
12+
'Note that the URLs (APP and API) that already exist in environment variables '
13+
f'({consts.CYCODE_API_URL_ENV_VAR_NAME} and {consts.CYCODE_APP_URL_ENV_VAR_NAME}) '
14+
'take precedent over these URLs; either update or remove the environment variables.'
15+
)
16+
_CREDENTIALS_UPDATED_SUCCESSFULLY_MESSAGE = 'Successfully configured CLI credentials! Saved to: {filename}'
17+
_CREDENTIALS_ARE_SET_IN_ENVIRONMENT_VARIABLES_MESSAGE = (
18+
'Note that the credentials that already exist in environment variables '
19+
f'({config.CYCODE_CLIENT_ID_ENV_VAR_NAME} and {config.CYCODE_CLIENT_SECRET_ENV_VAR_NAME}) '
20+
'take precedent over these credentials; either update or remove the environment variables.'
21+
)
22+
_CREDENTIALS_MANAGER = CredentialsManager()
23+
_CONFIGURATION_MANAGER = ConfigurationManager()
24+
25+
26+
@click.command(short_help='Initial command to configure your CLI client authentication.')
27+
def configure_command() -> None:
28+
"""Configure your CLI client authentication manually."""
29+
global_config_manager = _CONFIGURATION_MANAGER.global_config_file_manager
30+
31+
current_api_url = global_config_manager.get_api_url()
32+
current_app_url = global_config_manager.get_app_url()
33+
api_url = _get_api_url_input(current_api_url)
34+
app_url = _get_app_url_input(current_app_url)
35+
36+
config_updated = False
37+
if _should_update_value(current_api_url, api_url):
38+
global_config_manager.update_api_base_url(api_url)
39+
config_updated = True
40+
if _should_update_value(current_app_url, app_url):
41+
global_config_manager.update_app_base_url(app_url)
42+
config_updated = True
43+
44+
current_client_id, current_client_secret = _CREDENTIALS_MANAGER.get_credentials_from_file()
45+
client_id = _get_client_id_input(current_client_id)
46+
client_secret = _get_client_secret_input(current_client_secret)
47+
48+
credentials_updated = False
49+
if _should_update_value(current_client_id, client_id) or _should_update_value(current_client_secret, client_secret):
50+
credentials_updated = True
51+
_CREDENTIALS_MANAGER.update_credentials_file(client_id, client_secret)
52+
53+
if config_updated:
54+
click.echo(_get_urls_update_result_message())
55+
if credentials_updated:
56+
click.echo(_get_credentials_update_result_message())
57+
58+
59+
def _get_client_id_input(current_client_id: Optional[str]) -> Optional[str]:
60+
prompt_text = 'Cycode Client ID'
61+
62+
prompt_suffix = ' []: '
63+
if current_client_id:
64+
prompt_suffix = f' [{obfuscate_text(current_client_id)}]: '
65+
66+
new_client_id = click.prompt(text=prompt_text, prompt_suffix=prompt_suffix, default='', show_default=False)
67+
return new_client_id or current_client_id
68+
69+
70+
def _get_client_secret_input(current_client_secret: Optional[str]) -> Optional[str]:
71+
prompt_text = 'Cycode Client Secret'
72+
73+
prompt_suffix = ' []: '
74+
if current_client_secret:
75+
prompt_suffix = f' [{obfuscate_text(current_client_secret)}]: '
76+
77+
new_client_secret = click.prompt(text=prompt_text, prompt_suffix=prompt_suffix, default='', show_default=False)
78+
return new_client_secret or current_client_secret
79+
80+
81+
def _get_app_url_input(current_app_url: Optional[str]) -> str:
82+
prompt_text = 'Cycode APP URL'
83+
84+
default = consts.DEFAULT_CYCODE_APP_URL
85+
if current_app_url:
86+
default = current_app_url
87+
88+
return click.prompt(text=prompt_text, default=default, type=click.STRING)
89+
90+
91+
def _get_api_url_input(current_api_url: Optional[str]) -> str:
92+
prompt_text = 'Cycode API URL'
93+
94+
default = consts.DEFAULT_CYCODE_API_URL
95+
if current_api_url:
96+
default = current_api_url
97+
98+
return click.prompt(text=prompt_text, default=default, type=click.STRING)
99+
100+
101+
def _get_credentials_update_result_message() -> str:
102+
success_message = _CREDENTIALS_UPDATED_SUCCESSFULLY_MESSAGE.format(filename=_CREDENTIALS_MANAGER.get_filename())
103+
if _are_credentials_exist_in_environment_variables():
104+
return f'{success_message}. {_CREDENTIALS_ARE_SET_IN_ENVIRONMENT_VARIABLES_MESSAGE}'
105+
106+
return success_message
107+
108+
109+
def _are_credentials_exist_in_environment_variables() -> bool:
110+
client_id, client_secret = _CREDENTIALS_MANAGER.get_credentials_from_environment_variables()
111+
return any([client_id, client_secret])
112+
113+
114+
def _get_urls_update_result_message() -> str:
115+
success_message = _URLS_UPDATED_SUCCESSFULLY_MESSAGE.format(
116+
filename=_CONFIGURATION_MANAGER.global_config_file_manager.get_filename()
117+
)
118+
if _are_urls_exist_in_environment_variables():
119+
return f'{success_message}. {_URLS_ARE_SET_IN_ENVIRONMENT_VARIABLES_MESSAGE}'
120+
121+
return success_message
122+
123+
124+
def _are_urls_exist_in_environment_variables() -> bool:
125+
api_url = _CONFIGURATION_MANAGER.get_api_url_from_environment_variables()
126+
app_url = _CONFIGURATION_MANAGER.get_app_url_from_environment_variables()
127+
return any([api_url, app_url])
128+
129+
130+
def _should_update_value(
131+
old_value: Optional[str],
132+
new_value: Optional[str],
133+
) -> bool:
134+
if not new_value:
135+
return False
136+
137+
return old_value != new_value

cycode/cli/commands/ignore/__init__.py

Whitespace-only changes.

cycode/cli/user_settings/user_settings_commands.py renamed to cycode/cli/commands/ignore/ignore_command.py

Lines changed: 7 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,21 @@
1-
import os.path
1+
import os
22
import re
3-
from typing import Optional
43

54
import click
65

76
from cycode.cli import consts
87
from cycode.cli.config import config, configuration_manager
9-
from cycode.cli.user_settings.credentials_manager import CredentialsManager
108
from cycode.cli.utils.path_utils import get_absolute_path
11-
from cycode.cli.utils.string_utils import hash_string_to_sha256, obfuscate_text
9+
from cycode.cli.utils.string_utils import hash_string_to_sha256
1210
from cycode.cyclient import logger
1311

14-
CREDENTIALS_UPDATED_SUCCESSFULLY_MESSAGE = 'Successfully configured CLI credentials!'
15-
CREDENTIALS_ARE_SET_IN_ENVIRONMENT_VARIABLES_MESSAGE = (
16-
'Note that the credentials that already exist in environment'
17-
' variables (CYCODE_CLIENT_ID and CYCODE_CLIENT_SECRET) take'
18-
' precedent over these credentials; either update or remove '
19-
'the environment variables.'
20-
)
21-
credentials_manager = CredentialsManager()
22-
2312

24-
@click.command(
25-
short_help='Initial command to authenticate your CLI client with Cycode using a client ID and client secret.'
26-
)
27-
def set_credentials() -> None:
28-
"""Authenticates your CLI client with Cycode manually by using a client ID and client secret."""
29-
click.echo(f'Update credentials in file ({credentials_manager.get_filename()})')
30-
current_client_id, current_client_secret = credentials_manager.get_credentials_from_file()
31-
client_id = _get_client_id_input(current_client_id)
32-
client_secret = _get_client_secret_input(current_client_secret)
13+
def _is_path_to_ignore_exists(path: str) -> bool:
14+
return os.path.exists(path)
3315

34-
if not _should_update_credentials(current_client_id, current_client_secret, client_id, client_secret):
35-
return
3616

37-
credentials_manager.update_credentials_file(client_id, client_secret)
38-
click.echo(_get_credentials_update_result_message())
17+
def _is_package_pattern_valid(package: str) -> bool:
18+
return re.search('^[^@]+@[^@]+$', package) is not None
3919

4020

4121
@click.command(short_help='Ignores a specific value, path or rule ID.')
@@ -83,7 +63,7 @@ def set_credentials() -> None:
8363
required=False,
8464
help='Add an ignore rule to the global CLI config.',
8565
)
86-
def add_exclusions(
66+
def ignore_command(
8767
by_value: str, by_sha: str, by_path: str, by_rule: str, by_package: str, scan_type: str, is_global: bool
8868
) -> None:
8969
"""Ignores a specific value, path or rule ID."""
@@ -126,48 +106,3 @@ def add_exclusions(
126106
},
127107
)
128108
configuration_manager.add_exclusion(configuration_scope, scan_type, exclusion_type, exclusion_value)
129-
130-
131-
def _get_client_id_input(current_client_id: str) -> str:
132-
new_client_id = click.prompt(
133-
f'cycode client id [{_obfuscate_credential(current_client_id)}]', default='', show_default=False
134-
)
135-
136-
return new_client_id if new_client_id else current_client_id
137-
138-
139-
def _get_client_secret_input(current_client_secret: str) -> str:
140-
new_client_secret = click.prompt(
141-
f'cycode client secret [{_obfuscate_credential(current_client_secret)}]', default='', show_default=False
142-
)
143-
return new_client_secret if new_client_secret else current_client_secret
144-
145-
146-
def _get_credentials_update_result_message() -> str:
147-
if not _are_credentials_exist_in_environment_variables():
148-
return CREDENTIALS_UPDATED_SUCCESSFULLY_MESSAGE
149-
150-
return CREDENTIALS_UPDATED_SUCCESSFULLY_MESSAGE + ' ' + CREDENTIALS_ARE_SET_IN_ENVIRONMENT_VARIABLES_MESSAGE
151-
152-
153-
def _are_credentials_exist_in_environment_variables() -> bool:
154-
client_id, client_secret = credentials_manager.get_credentials_from_environment_variables()
155-
return client_id is not None or client_secret is not None
156-
157-
158-
def _should_update_credentials(
159-
current_client_id: str, current_client_secret: str, new_client_id: str, new_client_secret: str
160-
) -> bool:
161-
return current_client_id != new_client_id or current_client_secret != new_client_secret
162-
163-
164-
def _obfuscate_credential(credential: Optional[str]) -> str:
165-
return '' if not credential else obfuscate_text(credential)
166-
167-
168-
def _is_path_to_ignore_exists(path: str) -> bool:
169-
return os.path.exists(path)
170-
171-
172-
def _is_package_pattern_valid(package: str) -> bool:
173-
return re.search('^[^@]+@[^@]+$', package) is not None

cycode/cli/main.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from cycode import __version__
99
from cycode.cli import code_scanner
1010
from cycode.cli.auth.auth_command import authenticate
11+
from cycode.cli.commands.configure.configure_command import configure_command
12+
from cycode.cli.commands.ignore.ignore_command import ignore_command
1113
from cycode.cli.commands.report.report_command import report_command
1214
from cycode.cli.config import config
1315
from cycode.cli.consts import (
@@ -19,7 +21,6 @@
1921
)
2022
from cycode.cli.models import Severity
2123
from cycode.cli.user_settings.configuration_manager import ConfigurationManager
22-
from cycode.cli.user_settings.user_settings_commands import add_exclusions, set_credentials
2324
from cycode.cli.utils import scan_utils
2425
from cycode.cli.utils.get_api_client import get_scan_cycode_client
2526
from cycode.cli.utils.progress_bar import SCAN_PROGRESS_BAR_SECTIONS, get_progress_bar
@@ -183,8 +184,8 @@ def version(context: click.Context) -> None:
183184
commands={
184185
'scan': code_scan,
185186
'report': report_command,
186-
'configure': set_credentials,
187-
'ignore': add_exclusions,
187+
'configure': configure_command,
188+
'ignore': ignore_command,
188189
'auth': authenticate,
189190
'version': version,
190191
},

cycode/cli/user_settings/config_file_manager.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,12 @@ def get_exclude_detections_in_deleted_lines(self, command_scan_type: str) -> Opt
5252
command_scan_type, self.EXCLUDE_DETECTIONS_IN_DELETED_LINES
5353
)
5454

55-
def update_base_url(self, base_url: str) -> None:
56-
update_data = {self.ENVIRONMENT_SECTION_NAME: {self.API_URL_FIELD_NAME: base_url}}
55+
def update_api_base_url(self, api_url: str) -> None:
56+
update_data = {self.ENVIRONMENT_SECTION_NAME: {self.API_URL_FIELD_NAME: api_url}}
57+
self.write_content_to_file(update_data)
58+
59+
def update_app_base_url(self, app_url: str) -> None:
60+
update_data = {self.ENVIRONMENT_SECTION_NAME: {self.APP_URL_FIELD_NAME: app_url}}
5761
self.write_content_to_file(update_data)
5862

5963
def get_installation_id(self) -> Optional[str]:

cycode/cli/user_settings/configuration_manager.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,6 @@ def _merge_exclusions(self, local_exclusions: Dict, global_exclusions: Dict) ->
7676
keys = set(list(local_exclusions.keys()) + list(global_exclusions.keys()))
7777
return {key: local_exclusions.get(key, []) + global_exclusions.get(key, []) for key in keys}
7878

79-
def update_base_url(self, base_url: str, scope: str = 'local') -> None:
80-
config_file_manager = self.get_config_file_manager(scope)
81-
config_file_manager.update_base_url(base_url)
82-
8379
def get_or_create_installation_id(self) -> str:
8480
config_file_manager = self.get_config_file_manager()
8581

0 commit comments

Comments
 (0)