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

Add support for Azure Blob Storage connection string authentication #4649

Merged
merged 21 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
18e4fd2
Add support for Azure Blob Storage connection string authentication
suzusuzu May 20, 2022
9f26d73
Add Connection string document for Azure Blob Authorization type
suzusuzu May 20, 2022
d685623
modify CHANGELOG
suzusuzu May 20, 2022
0647d22
Merge branch 'develop' of https://github.com/openvinotoolkit/cvat int…
suzusuzu Aug 22, 2022
95e852f
Merge branch 'develop' of github.com:opencv/cvat into feature/blob_co…
suzusuzu Aug 30, 2022
10fcd96
Merge branch 'develop' into feature/blob_connection_string
bsekachev Sep 12, 2022
b9dcd85
Merge branch 'develop' of https://github.com/openvinotoolkit/cvat int…
suzusuzu Oct 13, 2022
6986367
Merge branch 'develop' into feature/blob_connection_string
suzusuzu Oct 19, 2022
ccfba07
Merge branch 'develop' into feature/blob_connection_string
nmanovic Nov 17, 2022
dcdeeb3
Merge branch 'develop' into feature/blob_connection_string
nmanovic Dec 10, 2022
a4f2857
Merge branch 'develop' into feature/blob_connection_string
Marishka17 Jan 17, 2023
80abf0e
Merge branch 'develop' into feature/blob_connection_string
nmanovic Jan 18, 2023
e628894
Resolve conflicts
Marishka17 Mar 13, 2023
8923d8c
Fix typo
Marishka17 Mar 13, 2023
a6d2b34
Add migration
Marishka17 Mar 13, 2023
70bfdbc
Apply comments
Marishka17 Mar 13, 2023
e1cd5fd
Merge branch 'develop' into feature/blob_connection_string
Marishka17 Mar 13, 2023
a39212e
Merge branch 'develop' into feature/blob_connection_string
nmanovic Mar 15, 2023
62ad57c
Merge branch 'develop' into feature/blob_connection_string
nmanovic Mar 18, 2023
5cac3d4
Update server schema
Marishka17 Mar 19, 2023
e94ecef
Fix schema
Marishka17 Mar 19, 2023
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Objects sorting option in the sidebar, by z order. Additional visualization when the sorting is applied
(<https://github.com/opencv/cvat/pull/5145>)
- Added YOLOv5 serverless function NVIDIA GPU support (<https://github.com/opencv/cvat/pull/4960>)
- Add support for Azure Blob Storage connection string authentication(<https://github.com/openvinotoolkit/cvat/pull/4649>)
- Mask tools are supported now (brush, eraser, polygon-plus, polygon-minus, returning masks
from online detectors & interactors) (<https://github.com/opencv/cvat/pull/4543>)
- Added Webhooks (<https://github.com/opencv/cvat/pull/4863>)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export default function CreateCloudStorageForm(props: Props): JSX.Element {
const [secretKeyVisibility, setSecretKeyVisibility] = useState(false);
const [sessionTokenVisibility, setSessionTokenVisibility] = useState(false);
const [accountNameVisibility, setAccountNameVisibility] = useState(false);
const [connectionStringVisibility, setConnectionStringVisibility] = useState(false);

const [manifestNames, setManifestNames] = useState<string[]>([]);

Expand Down Expand Up @@ -414,6 +415,25 @@ export default function CreateCloudStorageForm(props: Props): JSX.Element {
);
}

if (providerType === ProviderType.AZURE_CONTAINER && credentialsType === CredentialsType.CONNECTION_STRING) {
return (
<>
<Form.Item
label='Connection string'
name='connection_string'
rules={[{ required: true, message: 'Please, specify your connection string' }]}
{...internalCommonProps}
>
<Input.Password
maxLength={437}
Marishka17 marked this conversation as resolved.
Show resolved Hide resolved
visibilityToggle={connectionStringVisibility}
onChange={() => setConnectionStringVisibility(true)}
/>
</Form.Item>
</>
);
}

if (providerType === ProviderType.GOOGLE_CLOUD_STORAGE && credentialsType === CredentialsType.KEY_FILE_PATH) {
return (
<Form.Item
Expand Down Expand Up @@ -541,6 +561,7 @@ export default function CreateCloudStorageForm(props: Props): JSX.Element {
Account name and SAS token
</Select.Option>
<Select.Option value={CredentialsType.ANONYMOUS_ACCESS}>Anonymous access</Select.Option>
<Select.Option value={CredentialsType.CONNECTION_STRING}>Connection string</Select.Option>
</Select>
</Form.Item>

Expand Down
1 change: 1 addition & 0 deletions cvat-ui/src/utils/enums.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum CredentialsType {
KEY_SECRET_KEY_PAIR = 'KEY_SECRET_KEY_PAIR',
ACCOUNT_NAME_TOKEN_PAIR = 'ACCOUNT_NAME_TOKEN_PAIR',
ANONYMOUS_ACCESS = 'ANONYMOUS_ACCESS',
CONNECTION_STRING = 'CONNECTION_STRING',
KEY_FILE_PATH = 'KEY_FILE_PATH',
}

Expand Down
18 changes: 14 additions & 4 deletions cvat/apps/engine/cloud_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ def get_cloud_storage_instance(cloud_provider, resource, credentials, specific_a
instance = AzureBlobContainer(
container=resource,
account_name=credentials.account_name,
sas_token=credentials.session_token
sas_token=credentials.session_token,
connection_string=credentials.connection_string
)
elif cloud_provider == CloudProviderChoice.GOOGLE_CLOUD_STORAGE:
instance = GoogleCloudStorage(
Expand Down Expand Up @@ -382,10 +383,12 @@ class AzureBlobContainer(_CloudStorage):
class Effect:
pass

def __init__(self, container, account_name, sas_token=None):
def __init__(self, container, account_name, sas_token=None, connection_string=None):
Marishka17 marked this conversation as resolved.
Show resolved Hide resolved
super().__init__()
self._account_name = account_name
if sas_token:
if connection_string:
self._blob_service_client = BlobServiceClient.from_connection_string(connection_string)
elif sas_token:
self._blob_service_client = BlobServiceClient(account_url=self.account_url, credential=sas_token)
else:
self._blob_service_client = BlobServiceClient(account_url=self.account_url)
Expand Down Expand Up @@ -603,7 +606,7 @@ def supported_actions(self):
pass

class Credentials:
__slots__ = ('key', 'secret_key', 'session_token', 'account_name', 'key_file_path', 'credentials_type')
__slots__ = ('key', 'secret_key', 'session_token', 'account_name', 'key_file_path', 'credentials_type', 'connection_string')

def __init__(self, **credentials):
self.key = credentials.get('key', '')
Expand All @@ -612,6 +615,7 @@ def __init__(self, **credentials):
self.account_name = credentials.get('account_name', '')
self.key_file_path = credentials.get('key_file_path', '')
self.credentials_type = credentials.get('credentials_type', None)
self.connection_string = credentials.get('connection_string', None)

def convert_to_db(self):
converted_credentials = {
Expand All @@ -620,6 +624,7 @@ def convert_to_db(self):
CredentialsTypeChoice.ACCOUNT_NAME_TOKEN_PAIR : " ".join([self.account_name, self.session_token]),
CredentialsTypeChoice.KEY_FILE_PATH: self.key_file_path,
CredentialsTypeChoice.ANONYMOUS_ACCESS: "" if not self.account_name else self.account_name,
CredentialsTypeChoice.CONNECTION_STRING: self.connection_string,
}
return converted_credentials[self.credentials_type]

Expand All @@ -634,6 +639,8 @@ def convert_from_db(self, credentials):
self.account_name = credentials.get('value')
elif self.credentials_type == CredentialsTypeChoice.KEY_FILE_PATH:
self.key_file_path = credentials.get('value')
elif self.credentials_type == CredentialsTypeChoice.CONNECTION_STRING:
self.connection_string = credentials.get('value')
else:
raise NotImplementedError('Found {} not supported credentials type'.format(self.credentials_type))

Expand All @@ -657,6 +664,9 @@ def mapping_with_new_values(self, credentials):
elif self.credentials_type == CredentialsTypeChoice.KEY_FILE_PATH:
self.reset(exclusion={'key_file_path'})
self.key_file_path = credentials.get('key_file_path', self.key_file_path)
elif self.credentials_type == CredentialsTypeChoice.CONNECTION_STRING:
self.reset(exclusion={'connection_string'})
self.connection_string = credentials.get('connection_string', self.connection_string)
else:
raise NotImplementedError('Mapping credentials: unsupported credentials type')

Expand Down
1 change: 1 addition & 0 deletions cvat/apps/engine/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ class CredentialsTypeChoice(str, Enum):
ACCOUNT_NAME_TOKEN_PAIR = 'ACCOUNT_NAME_TOKEN_PAIR' # nosec
KEY_FILE_PATH = 'KEY_FILE_PATH'
ANONYMOUS_ACCESS = 'ANONYMOUS_ACCESS'
CONNECTION_STRING = 'CONNECTION_STRING'
Marishka17 marked this conversation as resolved.
Show resolved Hide resolved

@classmethod
def choices(cls):
Expand Down
10 changes: 6 additions & 4 deletions cvat/apps/engine/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1186,13 +1186,14 @@ class CloudStorageWriteSerializer(serializers.ModelSerializer):
key_file = serializers.FileField(required=False)
account_name = serializers.CharField(max_length=24, allow_blank=True, required=False)
manifests = ManifestSerializer(many=True, default=[])
connection_string = serializers.CharField(max_length=440, allow_blank=True, required=False)

class Meta:
model = models.CloudStorage
fields = (
'provider_type', 'resource', 'display_name', 'owner', 'credentials_type',
'created_date', 'updated_date', 'session_token', 'account_name', 'key',
'secret_key', 'key_file', 'specific_attributes', 'description', 'id',
'secret_key', 'connection_string', 'key_file', 'specific_attributes', 'description', 'id',
'manifests', 'organization'
)
read_only_fields = ('created_date', 'updated_date', 'owner', 'organization')
Expand All @@ -1210,8 +1211,8 @@ def validate_specific_attributes(self, value):
def validate(self, attrs):
provider_type = attrs.get('provider_type')
if provider_type == models.CloudProviderChoice.AZURE_CONTAINER:
if not attrs.get('account_name', ''):
raise serializers.ValidationError('Account name for Azure container was not specified')
if not attrs.get('account_name', '') and not attrs.get('connection_string', ''):
raise serializers.ValidationError('Account name or Connection string for Azure container was not specified')
Marishka17 marked this conversation as resolved.
Show resolved Hide resolved
return attrs

@staticmethod
Expand Down Expand Up @@ -1249,7 +1250,8 @@ def create(self, validated_data):
secret_key=validated_data.pop('secret_key', ''),
session_token=validated_data.pop('session_token', ''),
key_file_path=temporary_file,
credentials_type = validated_data.get('credentials_type')
credentials_type = validated_data.get('credentials_type'),
connection_string = validated_data.pop('connection_string', '')
)
details = {
'resource': validated_data.get('resource'),
Expand Down
2 changes: 1 addition & 1 deletion cvat/apps/engine/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2001,7 +2001,7 @@ class CloudStorageViewSet(viewsets.GenericViewSet, mixins.ListModelMixin,
queryset = CloudStorageModel.objects.all().prefetch_related('data')

search_fields = ('provider_type', 'display_name', 'resource',
'credentials_type', 'owner', 'description')
'credentials_type', 'owner', 'description', 'connection_string')
filter_fields = list(search_fields) + ['id']
ordering_fields = filter_fields
ordering = "-id"
Expand Down
3 changes: 3 additions & 0 deletions site/content/en/docs/manual/basics/attach-cloud-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,9 @@ and fill out the following form:
in this case, you only need the storage account name to gain anonymous access.
- `Account name` - storage account name.

- [`Connection string`](https://docs.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string) -
- `Connection String` - connection string.
Marishka17 marked this conversation as resolved.
Show resolved Hide resolved

</br>

- [**Google Cloud**](https://cloud.google.com/docs):
Expand Down