Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable SSL certificate validation by default #77

Merged
merged 3 commits into from
Nov 3, 2022
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
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,22 @@ Instantiating the `Geti` class will establish the connection and perform authent
)

```
Here, `"dummy_user"` and `"dummy_password"` should be replaced by your username and password for the Geti server.
Here, `"dummy_user"` and `"dummy_password"` should be replaced by your username and
password for the Geti server.


- **SSL certificate validation**

By default, the SDK verifies the SSL certificate of your server before establishing
a connection over HTTPS. If the certificate can't be validated, this will results in
an error and the SDK will not be able to connect to the server.

However, this may not be appropriate or desirable in all cases, for instance if your
Geti server does not have a certificate because you are running it in a private
network environment. In that case, certificate validation can be disabled by passing
`verify_certificate=False` to the `Geti` constructor. Please only disable certificate
validation in a secure environment!

#### Downloading and uploading projects

- **Project download** The following python snippet is a minimal example of how to
Expand Down
12 changes: 12 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ the examples, simply:
>
> In case both a TOKEN and USERNAME/PASSWORD variables are provided, the SDK
> will default to using the TOKEN since this method is considered more secure.
> #### SSL Certificate verification
> By default, the SDK verifies the SSL certificate of your server before establishing
> a connection over HTTPS. If the certificate can't be validated, this will results in
> an error and the SDK will not be able to connect to the server.
>
> However, this may not be appropriate or desirable in all cases, for instance if your
> Geti server does not have a certificate because you are running it in a private
> network environment. In that case, certificate validation can be disabled by adding
> the following variable to the `.env` file:
> ```shell
> VERIFY_CERT=0
> ```

## Creating a project from an existing dataset
#### COCO/Datumaro examples
Expand Down
8 changes: 4 additions & 4 deletions examples/create_coco_project_single_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
from geti_sdk.utils import get_server_details_from_env

if __name__ == "__main__":
# Get credentials from .env file
hostname, authentication = get_server_details_from_env()
# Get the server configuration from .env file
server_config = get_server_details_from_env()

# --------------------------------------------------
# Configuration section
# --------------------------------------------------
# Set up the Geti instance with server hostname and authentication details
geti = Geti(host=hostname, **authentication)
# Set up the Geti instance with the server configuration details
geti = Geti(server_config=server_config)

# Dataset configuration
NUMBER_OF_IMAGES_TO_UPLOAD = 75
Expand Down
8 changes: 4 additions & 4 deletions examples/create_coco_project_task_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
from geti_sdk.utils import get_server_details_from_env, get_task_types_by_project_type

if __name__ == "__main__":
# Get credentials from .env file
hostname, authentication = get_server_details_from_env()
# Get the server configuration from .env file
server_config = get_server_details_from_env()

# --------------------------------------------------
# Configuration section
# --------------------------------------------------
# Set up the Geti instance with server hostname and authentication details
geti = Geti(host=hostname, **authentication)
# Set up the Geti instance with the server configuration details
geti = Geti(server_config=server_config)

# Dataset configuration
NUMBER_OF_IMAGES_TO_UPLOAD = 75
Expand Down
8 changes: 4 additions & 4 deletions examples/create_demo_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
from geti_sdk.utils import get_server_details_from_env

if __name__ == "__main__":
# Get credentials from .env file
hostname, authentication = get_server_details_from_env()
# Get the server configuration from .env file
server_config = get_server_details_from_env()

# --------------------------------------------------
# Configuration section
# --------------------------------------------------
# Set up the Geti instance with server hostname and authentication details
geti = Geti(host=hostname, **authentication)
# Set up the Geti instance with the server configuration details
geti = Geti(server_config=server_config)

# Dataset configuration
NUMBER_OF_IMAGES_TO_UPLOAD = 100
Expand Down
8 changes: 4 additions & 4 deletions examples/upload_and_predict_from_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ def rotate_image(image: np.ndarray, angle: float) -> np.ndarray:


if __name__ == "__main__":
# Get credentials from .env file
hostname, authentication = get_server_details_from_env()
# Get the server configuration from .env file
server_config = get_server_details_from_env()

# --------------------------------------------------
# Configuration section
# --------------------------------------------------
# Set up the Geti instance with server hostname and authentication details
geti = Geti(host=hostname, **authentication)
# Set up the Geti instance with the server configuration details
geti = Geti(server_config=server_config)

# `PROJECT_NAME` is the name of the project to which the media should be uploaded,
# and from which predictions can be requested. A project with this name should
Expand Down
8 changes: 4 additions & 4 deletions examples/upload_and_predict_media_from_folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
from geti_sdk.utils import get_server_details_from_env

if __name__ == "__main__":
# Get credentials from .env file
hostname, authentication = get_server_details_from_env()
# Get the server configuration from .env file
server_config = get_server_details_from_env()

# --------------------------------------------------
# Configuration section
# --------------------------------------------------
# Set up the Geti instance with server hostname and authentication details
geti = Geti(host=hostname, **authentication)
# Set up the Geti instance with the server configuration details
geti = Geti(server_config=server_config)

# `FOLDER_WITH_MEDIA` is the path to the directory with images and videos that
# should be uploaded to the GETi cluster
Expand Down
91 changes: 63 additions & 28 deletions geti_sdk/geti.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,19 @@ class Geti:
uploading, as well as project deployment. Initializing the class will establish a
HTTP session to the Intel® Geti™ server, and requires authentication.

NOTE: The `Geti` instance can either be initialized using user credentials (`username`
and `password`), or using a personal access token (`token`). Arguments for either
one of these two options must be passed, otherwise a TypeError will be raised.
NOTE: The `Geti` instance can either be initialized in the following ways:

1. Using user credentials. This requires: `host`, `username` and `password` to
be passed as input
2. Using a personal access token. This requires: `host` and `token` to be
passed as input
3. Using a :py:class:`~geti_sdk.http_session.server_config.ServerTokenConfig`
or :py:class:`~geti_sdk.http_session.server_config.ServerCredentialConfig`
instance that contains the full configuration for the Geti server to
communicate with. This requires `server_config` to be passed as input.

Arguments for either one of these options must be passed, otherwise a TypeError
will be raised.

:param host: IP address or URL at which the cluster can be reached, for example
'https://0.0.0.0' or 'https://sc_example.intel.com'
Expand All @@ -86,17 +96,22 @@ class Geti:
'https': http://proxy-server.com:<https_port_number>
},
if set to None (the default), no proxy settings will be used.
:param server_config: ServerConfiguration instance holding the full details for
the Geti server to communicate with
"""

def __init__(
self,
host: str,
host: Optional[str] = None,
username: Optional[str] = None,
password: Optional[str] = None,
token: Optional[str] = None,
workspace_id: Optional[str] = None,
verify_certificate: bool = False,
verify_certificate: bool = True,
proxies: Optional[Dict[str, str]] = None,
server_config: Optional[
Union[ServerTokenConfig, ServerCredentialConfig]
] = None,
):
# Set up default logging for the SDK.
if not logging.root.handlers:
Expand All @@ -106,32 +121,52 @@ def __init__(
format=DEFAULT_LOG_FORMAT,
)

# Set up server configuration with either token or credential authentication
if token is not None:
server_config = ServerTokenConfig(
host=host,
token=token,
proxies=proxies,
has_valid_certificate=verify_certificate,
# Validate input parameters
if host is None and server_config is None:
raise TypeError(
"__init__ missing required keyword arguments: Either `host` or "
"`server_config` must be specified."
)
if username is not None or password is not None:
warnings.warn(
"Both a personal access token and credentials were passed to "
"Geti, using token authentication."

if server_config is None:
# Set up server configuration with either token or credential authentication
if token is not None:
server_config = ServerTokenConfig(
host=host,
token=token,
proxies=proxies,
has_valid_certificate=verify_certificate,
)
if username is not None or password is not None:
warnings.warn(
"Both a personal access token and credentials were passed to "
"Geti, using token authentication."
)
elif username is not None and password is not None:
server_config = ServerCredentialConfig(
host=host,
username=username,
password=password,
proxies=proxies,
has_valid_certificate=verify_certificate,
)
else:
raise TypeError(
"__init__ missing required keyword arguments: Either `username` and "
"`password` or `token` must be specified."
)
elif username is not None and password is not None:
server_config = ServerCredentialConfig(
host=host,
username=username,
password=password,
proxies=proxies,
has_valid_certificate=verify_certificate,
)
else:
raise TypeError(
"__init__ missing required keyword arguments: Either `username` and "
"`password` or `token` must be specified."
)
if host is not None:
warnings.warn(
"Both `host` and `server_config` were passed to `Geti`, ignoring "
"the value set for `host`."
)
if proxies is not None:
warnings.warn(
"Both `proxies` and `server_config` were passed to `Geti`, "
"ignoring the value set for `proxies`. If you want to use proxies "
"please update the `server_config` accordingly."
)

# Initialize session and get workspace id
self.session = GetiSession(
Expand Down
15 changes: 14 additions & 1 deletion geti_sdk/http_session/geti_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ def authenticate(self, verbose: bool = True):
"""
try:
login_path = self._get_initial_login_url()
except requests.exceptions.SSLError as error:
raise requests.exceptions.SSLError(
f"Connection to Intel® Geti™ server at '{self.config.host}' failed, "
f"the server address can be resolved but the SSL certificate could not "
f"be verified. \n Full error description: {error.args[-1]}"
)
except requests.exceptions.ConnectionError as error:
if "dummy" in self.config.password or "dummy" in self.config.username:
raise ValueError(
Expand Down Expand Up @@ -243,7 +249,14 @@ def get_rest_response(
if not self.use_token:
request_params.update({"cookies": self._cookies})

response = self.request(**request_params, **self._proxies)
try:
response = self.request(**request_params, **self._proxies)
except requests.exceptions.SSLError as error:
raise requests.exceptions.SSLError(
f"Connection to Intel® Geti™ server at '{self.config.host}' failed, "
f"the server address can be resolved but the SSL certificate could not "
f"be verified. \n Full error description: {error.args[-1]}"
)

if (
response.status_code not in SUCCESS_STATUS_CODES
Expand Down
2 changes: 1 addition & 1 deletion geti_sdk/http_session/server_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class ServerCredentialConfig(ServerConfig):
password: str


@attrs.define
@attrs.define(slots=False)
class ServerTokenConfig(ServerConfig):
"""
Configuration for an Intel® Geti™ server that uses a personal access token
Expand Down
Loading