Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
57c7299
feat(sdk): AuthStore profile CRUD, default profile, default server
Pr1ncee Jun 23, 2026
a4d43e8
feat(sdk): add changelog
Pr1ncee Jun 24, 2026
2f7ba12
feat(sdk): delete changelog
Pr1ncee Jun 24, 2026
0855ee5
Merge branch 'feat/sdk-persistent-auth' into feat/sdk-persistent-auth-t2
Pr1ncee Jun 24, 2026
03205b2
feat(sdk): unify OS platform checks
Pr1ncee Jun 24, 2026
fd134d9
feat(sdk): adjust permissions
Pr1ncee Jun 24, 2026
843be99
Merge branch 'feat/sdk-persistent-auth' into feat/sdk-persistent-auth-t2
Pr1ncee Jun 25, 2026
cb29a2c
feat(sdk): remove unnecessary file operations on each call
Pr1ncee Jun 25, 2026
e977421
Merge branch 'develop' into feat/sdk-persistent-auth-t2
Pr1ncee Jun 25, 2026
1853a32
refactor: move auth factories into cvat_sdk.core.auth, re-export from…
Pr1ncee Jun 25, 2026
e9cc634
feat(sdk): add resolve_server_host and add_cli_parser_args
Pr1ncee Jun 25, 2026
7f18d6e
feat(sdk): add make_client_from_profile
Pr1ncee Jun 25, 2026
5b3a35b
feat(sdk): add make_client_from_cli with full resolution order
Pr1ncee Jun 25, 2026
50d8675
feat(sdk): export persistent-auth public API
Pr1ncee Jun 25, 2026
98724be
feat(sdk): fix lint
Pr1ncee Jun 25, 2026
951b3b5
feat(sdk): add changelog file
Pr1ncee Jun 25, 2026
cb16ecc
feat(sdk): refactor code
Pr1ncee Jun 25, 2026
5abd3d0
feat(sdk): hotfix
Pr1ncee Jun 25, 2026
fb6e0a5
feat(sdk): fix lint
Pr1ncee Jun 25, 2026
d6eabc2
feat(sdk): fix test
Pr1ncee Jun 25, 2026
6d453fb
Update cvat-sdk/cvat_sdk/core/auth.py
Pr1ncee Jun 29, 2026
f5b5caa
feat(sdk): fix requested changes
Pr1ncee Jun 29, 2026
e477396
Merge branch 'feat/sdk-persistent-auth-t3' of https://github.com/cvat…
Pr1ncee Jun 29, 2026
7b6b6db
feat(sdk): fix isort lint
Pr1ncee Jun 29, 2026
b8d9dab
feat(sdk): fix test
Pr1ncee Jun 30, 2026
fdfbeed
feat(sdk): fix requested changes
Pr1ncee Jun 30, 2026
5c60637
feat(sdk): fix requested changes
Pr1ncee Jun 30, 2026
1898397
feat(sdk): fix lint
Pr1ncee Jun 30, 2026
c5f29f4
feat(sdk): fix requested change
Pr1ncee Jun 30, 2026
34ae8cd
feat(sdk): fix requested change
Pr1ncee Jun 30, 2026
97a89a8
feat(sdk): fix requested change
Pr1ncee Jun 30, 2026
95e8ad1
Fix requested changes
Pr1ncee Jul 2, 2026
434b3a5
Update tests/python/sdk/test_auth_store.py
Pr1ncee Jul 7, 2026
e02890d
Update cvat-sdk/cvat_sdk/core/auth.py
Pr1ncee Jul 7, 2026
8ce4706
Update tests
Pr1ncee Jul 7, 2026
4dca7fc
Add TODO
Pr1ncee Jul 7, 2026
45c23ef
Merge branch 'feat/sdk-persistent-auth-t2' into feat/sdk-persistent-a…
Pr1ncee Jul 7, 2026
9410277
Merge branch 'develop' into feat/sdk-persistent-auth-t3
Pr1ncee Jul 7, 2026
f6b9f50
Fix missing
Pr1ncee Jul 7, 2026
08077e9
Merge branch 'develop' into feat/sdk-persistent-auth-t3
Pr1ncee Jul 8, 2026
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
8 changes: 8 additions & 0 deletions changelog.d/20260625_000000_andrei_sdk_persistent_auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
### Added

- \[SDK\] Persistent authentication support with saved profiles:
`make_client_from_profile`, `make_client_from_cli`, and
`resolve_server_host` helpers let SDK users save server + credential profiles
to disk and instantiate a `Client` from a profile name without re-entering
credentials
(<https://github.com/cvat-ai/cvat/pull/10824>)
113 changes: 14 additions & 99 deletions cvat-cli/src/cvat_cli/_internal/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,23 @@
# SPDX-License-Identifier: MIT

import argparse
import getpass
import importlib
import importlib.util
import logging
import os
import sys
import textwrap
from collections.abc import Callable
from http.client import HTTPConnection
from pathlib import Path
from typing import Any

import attrs
import cvat_sdk.auto_annotation as cvataa
from cvat_sdk.core.client import (
AccessTokenCredentials,
Client,
Config,
Credentials,
PasswordCredentials,
from cvat_sdk.core.auth import (
ClientAuthParameters,
configure_client_auth_arguments,
make_client_from_cli,
)
from cvat_sdk.core.client import Client
from cvat_sdk.core.exceptions import AuthStoreError

from ..version import VERSION
from .parsers import BuildDictAction, parse_function_parameter
Expand All @@ -35,82 +31,9 @@ class CriticalError(Exception):
pass


CVAT_ACCESS_TOKEN_ENV_VAR = "CVAT_ACCESS_TOKEN" # nosec - a variable name declaration


def default_auth_factory() -> Callable[[str], Credentials]:
"""
Try to read the CVAT_ACCESS_TOKEN environment variable for a Personal Access Token (PAT).
If there is no value, try using the current user and asking for the password.
"""

token = os.getenv(CVAT_ACCESS_TOKEN_ENV_VAR)
if token is not None:
return lambda _: AccessTokenCredentials(token)

return get_auth_factory(getpass.getuser())


def get_auth_factory(s: str) -> Callable[[str], Credentials]:
"""
Parse a USER[:PASS] string and return a callable that takes the server URL
and returns auth credentials for that URL.
The callable will prompt the user for the password if none was initially supplied in the
input string and in the PASS env variable.
"""

user, _, password = s.partition(":")
if not password:
password = os.environ.get("PASS")

if password:
return lambda _: PasswordCredentials(user, password)
else:
return lambda url: PasswordCredentials(
user, getpass.getpass(f"Password for {user} at {url}: ")
)


def configure_common_arguments(parser: argparse.ArgumentParser) -> None:
parser.add_argument("--version", action="version", version=VERSION)
parser.add_argument(
"--insecure",
action="store_true",
help="Allows to disable SSL certificate check",
)

parser.add_argument(
"--auth",
type=get_auth_factory,
metavar="USER[:PASS]",
default=default_auth_factory(),
help=textwrap.dedent("""\
User and password to use for authentication;
defaults to the current user and supports the PASS
environment variable or password prompt.
A Personal Access Token (PAT) can be generated on the server
and specified in the {} environment variable instead.
(default user: {}).
""").format(CVAT_ACCESS_TOKEN_ENV_VAR, getpass.getuser()),
)
parser.add_argument(
"--server-host", type=str, default="http://localhost", help="host (default: %(default)s)"
)
parser.add_argument(
"--server-port",
type=int,
default=None,
help="port (default: 80 for http and 443 for https connections)",
)
parser.add_argument(
"--organization",
"--org",
metavar="SLUG",
help="""short name (slug) of the organization
to use when listing or creating resources;
set to blank string to use the personal workspace
(default: list all accessible objects, create in personal workspace)""",
)
configure_client_auth_arguments(parser)
parser.add_argument(
"--debug",
action="store_const",
Expand All @@ -135,24 +58,16 @@ def configure_logger(logger: logging.Logger, parsed_args: argparse.Namespace) ->


def build_client(parsed_args: argparse.Namespace, logger: logging.Logger) -> Client:
config = Config(verify_ssl=not popattr(parsed_args, "insecure"))

url = popattr(parsed_args, "server_host")
if server_port := popattr(parsed_args, "server_port"):
url += f":{server_port}"
auth_args = ClientAuthParameters.from_namespace(parsed_args)
for field in attrs.fields(ClientAuthParameters):
popattr(parsed_args, field.name)

client = Client(
url=url,
logger=logger,
config=config,
check_server_version=False, # version is checked after auth to support versions < 2.3
)
try:
client = make_client_from_cli(auth_args, logger=logger)
except AuthStoreError as e:
raise CriticalError(str(e)) from e

client.login(popattr(parsed_args, "auth")(client.api_client.configuration.host))
client.check_server_version(fail_if_unsupported=False)

client.organization_slug = popattr(parsed_args, "organization")

return client


Expand Down
9 changes: 9 additions & 0 deletions cvat-sdk/cvat_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,14 @@
#
# SPDX-License-Identifier: MIT

from cvat_sdk.core.auth import (
AuthStore,
ClientAuthParameters,
ProfileEntry,
configure_client_auth_arguments,
get_auth_store_path,
make_client_from_cli,
make_client_from_profile,
)
from cvat_sdk.core.client import Client, Config, make_client
from cvat_sdk.version import VERSION as __version__
9 changes: 9 additions & 0 deletions cvat-sdk/cvat_sdk/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,14 @@
#
# SPDX-License-Identifier: MIT

from cvat_sdk.core.auth import (
AuthStore,
ClientAuthParameters,
ProfileEntry,
configure_client_auth_arguments,
get_auth_store_path,
make_client_from_cli,
make_client_from_profile,
)
from cvat_sdk.core.client import Client, Config, make_client
from cvat_sdk.version import VERSION as __version__
Loading
Loading