feat: 🎸 Add support for output fields (#3268)
* feat: 🎸 Add support for output fields * refactor: 💡 PR feedback
This commit is contained in:
1 parent
e0b80b1176
commit
7fa59bfd5d
6 files changed
+970
-254
No files matched your search
@@ -5,147 +5,93 @@
|
||||
|
||||
export const grantsSchemaData = {
|
||||
resource_types: [
|
||||
{
|
||||
type: 'auth-method',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['change-state', 'authenticate', 'read', 'update', 'delete'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['ampw', 'amoidc', 'amldap'],
|
||||
},
|
||||
{
|
||||
type: 'account',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: [
|
||||
'set-password',
|
||||
'change-password',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['acctoidc', 'acctldap', 'apw', 'acctpw'],
|
||||
parent_type: 'auth-method',
|
||||
},
|
||||
{
|
||||
type: 'host',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['hst', 'hplg', 'h'],
|
||||
parent_type: 'host-catalog',
|
||||
},
|
||||
{
|
||||
type: 'session',
|
||||
collection_actions: ['list'],
|
||||
id_actions: ['read', 'read:self', 'cancel', 'cancel:self'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['s'],
|
||||
},
|
||||
{
|
||||
type: 'credential-store',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['csst', 'cs', 'csvlt'],
|
||||
id_prefixes: ['csvlt', 'csst', 'cs'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'attributes',
|
||||
'vault_credential_store_attributes',
|
||||
'authorized_actions',
|
||||
'authorized_collection_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'alias',
|
||||
type: 'credential',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['global'],
|
||||
id_prefixes: ['alt'],
|
||||
id_actions: ['update', 'delete', 'read'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: [
|
||||
'cdvlt',
|
||||
'credp',
|
||||
'credup',
|
||||
'credjson',
|
||||
'cred',
|
||||
'credupd',
|
||||
'credspk',
|
||||
],
|
||||
parent_type: 'credential-store',
|
||||
output_fields: [
|
||||
'id',
|
||||
'credential_store_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'attributes',
|
||||
'username_password_attributes',
|
||||
'ssh_private_key_attributes',
|
||||
'json_attributes',
|
||||
'username_password_domain_attributes',
|
||||
'password_attributes',
|
||||
'authorized_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'user',
|
||||
collection_actions: ['create', 'list'],
|
||||
collection_actions: ['list', 'create'],
|
||||
id_actions: [
|
||||
'remove-accounts',
|
||||
'list-resolvable-aliases',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'add-accounts',
|
||||
'set-accounts',
|
||||
'remove-accounts',
|
||||
],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['u'],
|
||||
},
|
||||
{
|
||||
type: 'auth-token',
|
||||
collection_actions: ['list'],
|
||||
id_actions: ['read', 'read:self', 'delete', 'delete:self'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['at'],
|
||||
},
|
||||
{
|
||||
type: 'target',
|
||||
collection_actions: ['list', 'create'],
|
||||
id_actions: [
|
||||
'add-credential-sources',
|
||||
'remove-credential-sources',
|
||||
'delete',
|
||||
'set-host-sources',
|
||||
'set-credential-sources',
|
||||
'authorize-session',
|
||||
'read',
|
||||
'update',
|
||||
'add-host-sources',
|
||||
'remove-host-sources',
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'account_ids',
|
||||
'accounts',
|
||||
'authorized_actions',
|
||||
'login_name',
|
||||
'full_name',
|
||||
'email',
|
||||
'primary_account_id',
|
||||
],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['trdp', 'ttcp', 'tssh'],
|
||||
},
|
||||
{
|
||||
type: 'worker',
|
||||
collection_actions: [
|
||||
'create:controller-led',
|
||||
'create:worker-led',
|
||||
'list',
|
||||
'read-certificate-authority',
|
||||
'reinitialize-certificate-authority',
|
||||
],
|
||||
id_actions: [
|
||||
'set-worker-tags',
|
||||
'remove-worker-tags',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'add-worker-tags',
|
||||
],
|
||||
scopes: ['global'],
|
||||
id_prefixes: ['w'],
|
||||
},
|
||||
{
|
||||
type: 'managed-group',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['update', 'delete', 'read'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['mgoidc', 'mgldap'],
|
||||
parent_type: 'auth-method',
|
||||
},
|
||||
{
|
||||
type: 'credential-library',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['clvllt', 'clvsclt', 'clvlt'],
|
||||
parent_type: 'credential-store',
|
||||
},
|
||||
{
|
||||
type: 'credential',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: [
|
||||
'credupd',
|
||||
'credp',
|
||||
'credspk',
|
||||
'cdvlt',
|
||||
'credjson',
|
||||
'cred',
|
||||
'credup',
|
||||
],
|
||||
parent_type: 'credential-store',
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
@@ -160,33 +106,417 @@ export const grantsSchemaData = {
|
||||
],
|
||||
scopes: ['global', 'org', 'project'],
|
||||
id_prefixes: ['g'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'member_ids',
|
||||
'members',
|
||||
'authorized_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'auth-method',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['authenticate', 'read', 'update', 'delete', 'change-state'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['amldap', 'ampw', 'amoidc'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'attributes',
|
||||
'password_auth_method_attributes',
|
||||
'oidc_auth_methods_attributes',
|
||||
'ldap_auth_methods_attributes',
|
||||
'is_primary',
|
||||
'authorized_actions',
|
||||
'authorized_collection_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'account',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: [
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'set-password',
|
||||
'change-password',
|
||||
],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['apw', 'acctoidc', 'acctpw', 'acctldap'],
|
||||
parent_type: 'auth-method',
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'auth_method_id',
|
||||
'attributes',
|
||||
'password_account_attributes',
|
||||
'oidc_account_attributes',
|
||||
'ldap_account_attributes',
|
||||
'managed_group_ids',
|
||||
'authorized_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'host-catalog',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['update', 'delete', 'read'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['hcst', 'hcplg', 'hc'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'plugin_id',
|
||||
'plugin',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'attributes',
|
||||
'secrets',
|
||||
'secrets_hmac',
|
||||
'worker_filter',
|
||||
'authorized_actions',
|
||||
'authorized_collection_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'target',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: [
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'remove-host-sources',
|
||||
'remove-credential-sources',
|
||||
'add-host-sources',
|
||||
'set-host-sources',
|
||||
'add-credential-sources',
|
||||
'set-credential-sources',
|
||||
'authorize-session',
|
||||
],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['ttcp', 'tssh', 'trdp'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'host_source_ids',
|
||||
'host_sources',
|
||||
'session_max_seconds',
|
||||
'session_connection_limit',
|
||||
'worker_filter',
|
||||
'egress_worker_filter',
|
||||
'ingress_worker_filter',
|
||||
'brokered_credential_source_ids',
|
||||
'brokered_credential_sources',
|
||||
'injected_application_credential_source_ids',
|
||||
'injected_application_credential_sources',
|
||||
'attributes',
|
||||
'tcp_target_attributes',
|
||||
'ssh_target_attributes',
|
||||
'rdp_target_attributes',
|
||||
'authorized_actions',
|
||||
'address',
|
||||
'aliases',
|
||||
'with_aliases',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'worker',
|
||||
collection_actions: [
|
||||
'create:controller-led',
|
||||
'create:worker-led',
|
||||
'list',
|
||||
'read-certificate-authority',
|
||||
'reinitialize-certificate-authority',
|
||||
],
|
||||
id_actions: [
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'add-worker-tags',
|
||||
'set-worker-tags',
|
||||
'remove-worker-tags',
|
||||
],
|
||||
scopes: ['global'],
|
||||
id_prefixes: ['w'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'address',
|
||||
'canonical_tags',
|
||||
'config_tags',
|
||||
'last_status_time',
|
||||
'worker_generated_auth_token',
|
||||
'controller_generated_activation_token',
|
||||
'active_connection_count',
|
||||
'type',
|
||||
'api_tags',
|
||||
'release_version',
|
||||
'directly_connected_downstream_workers',
|
||||
'authorized_actions',
|
||||
'local_storage_state',
|
||||
'remote_storage_state',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'credential-library',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['delete', 'read', 'update'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['clvllt', 'clvsclt', 'clvlt'],
|
||||
parent_type: 'credential-store',
|
||||
output_fields: [
|
||||
'id',
|
||||
'credential_store_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'attributes',
|
||||
'vault_credential_library_attributes',
|
||||
'vault_ssh_certificate_credential_library_attributes',
|
||||
'vault_generic_credential_library_attributes',
|
||||
'vault_ldap_credential_library_attributes',
|
||||
'authorized_actions',
|
||||
'credential_type',
|
||||
'credential_mapping_overrides',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'billing',
|
||||
collection_actions: ['monthly-active-users'],
|
||||
scopes: ['global'],
|
||||
},
|
||||
{
|
||||
type: 'alias',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['global'],
|
||||
id_prefixes: ['alt'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'value',
|
||||
'destination_id',
|
||||
'type',
|
||||
'attributes',
|
||||
'target_alias_attributes',
|
||||
'authorized_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'auth-token',
|
||||
collection_actions: ['list'],
|
||||
id_actions: ['delete:self', 'read', 'read:self', 'delete'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['at'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'token',
|
||||
'user_id',
|
||||
'auth_method_id',
|
||||
'account_id',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'approximate_last_used_time',
|
||||
'expiration_time',
|
||||
'authorized_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'host',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['hplg', 'hst', 'h'],
|
||||
parent_type: 'host-catalog',
|
||||
output_fields: [
|
||||
'id',
|
||||
'host_catalog_id',
|
||||
'scope',
|
||||
'plugin',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'host_set_ids',
|
||||
'attributes',
|
||||
'static_host_attributes',
|
||||
'ip_addresses',
|
||||
'dns_names',
|
||||
'external_id',
|
||||
'external_name',
|
||||
'authorized_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'storage-bucket',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['sb'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'plugin_id',
|
||||
'plugin',
|
||||
'name',
|
||||
'description',
|
||||
'bucket_name',
|
||||
'bucket_prefix',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'attributes',
|
||||
'secrets',
|
||||
'secrets_hmac',
|
||||
'worker_filter',
|
||||
'storage_bucket_credential_id',
|
||||
'authorized_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'policy',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['delete', 'read', 'update'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['pst'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'type',
|
||||
'version',
|
||||
'attributes',
|
||||
'storage_policy_attributes',
|
||||
'authorized_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'scope',
|
||||
collection_actions: [
|
||||
'list-keys',
|
||||
'rotate-keys',
|
||||
'list-key-version-destruction-jobs',
|
||||
'destroy-key-version',
|
||||
'create',
|
||||
'list',
|
||||
],
|
||||
id_actions: [
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'attach-storage-policy',
|
||||
'detach-storage-policy',
|
||||
],
|
||||
scopes: ['global', 'org', 'project'],
|
||||
id_prefixes: ['p', 'global', 'o'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'primary_auth_method_id',
|
||||
'authorized_actions',
|
||||
'authorized_collection_actions',
|
||||
'storage_policy_id',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'role',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: [
|
||||
'add-grant-scopes',
|
||||
'read',
|
||||
'update',
|
||||
'add-principals',
|
||||
'add-grants',
|
||||
'remove-principals',
|
||||
'set-grant-scopes',
|
||||
'remove-grant-scopes',
|
||||
'read',
|
||||
'delete',
|
||||
'set-principals',
|
||||
'remove-principals',
|
||||
'add-grants',
|
||||
'set-grants',
|
||||
'remove-grants',
|
||||
'add-grant-scopes',
|
||||
],
|
||||
scopes: ['global', 'org', 'project'],
|
||||
id_prefixes: ['r'],
|
||||
},
|
||||
{
|
||||
type: 'host-catalog',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['hcst', 'hc', 'hcplg'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope_id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'grant_scope_ids',
|
||||
'principal_ids',
|
||||
'principals',
|
||||
'grant_strings',
|
||||
'grants',
|
||||
'authorized_actions',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'host-set',
|
||||
@@ -200,47 +530,54 @@ export const grantsSchemaData = {
|
||||
'update',
|
||||
],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['hsst', 'hsplg', 'hs'],
|
||||
id_prefixes: ['hs', 'hsplg', 'hsst'],
|
||||
parent_type: 'host-catalog',
|
||||
},
|
||||
{
|
||||
type: 'storage-bucket',
|
||||
collection_actions: ['list', 'create'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['sb'],
|
||||
},
|
||||
{
|
||||
type: 'policy',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['read', 'update', 'delete'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['pst'],
|
||||
},
|
||||
{
|
||||
type: 'billing',
|
||||
collection_actions: ['monthly-active-users'],
|
||||
scopes: ['global'],
|
||||
},
|
||||
{
|
||||
type: 'scope',
|
||||
collection_actions: [
|
||||
'destroy-key-version',
|
||||
'create',
|
||||
'list',
|
||||
'list-keys',
|
||||
'rotate-keys',
|
||||
'list-key-version-destruction-jobs',
|
||||
output_fields: [
|
||||
'id',
|
||||
'host_catalog_id',
|
||||
'scope',
|
||||
'plugin',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'host_ids',
|
||||
'preferred_endpoints',
|
||||
'sync_interval_seconds',
|
||||
'attributes',
|
||||
'authorized_actions',
|
||||
],
|
||||
id_actions: [
|
||||
'attach-storage-policy',
|
||||
'detach-storage-policy',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
},
|
||||
{
|
||||
type: 'session',
|
||||
collection_actions: ['list'],
|
||||
id_actions: ['cancel', 'cancel:self', 'read', 'read:self'],
|
||||
scopes: ['project'],
|
||||
id_prefixes: ['s'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'target_id',
|
||||
'scope',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'expiration_time',
|
||||
'auth_token_id',
|
||||
'user_id',
|
||||
'host_set_id',
|
||||
'host_id',
|
||||
'scope_id',
|
||||
'endpoint',
|
||||
'states',
|
||||
'status',
|
||||
'certificate',
|
||||
'termination_reason',
|
||||
'authorized_actions',
|
||||
'connections',
|
||||
],
|
||||
scopes: ['global', 'org', 'project'],
|
||||
id_prefixes: ['o', 'global', 'p'],
|
||||
},
|
||||
{
|
||||
type: 'session-recording',
|
||||
@@ -248,6 +585,53 @@ export const grantsSchemaData = {
|
||||
id_actions: ['read', 'download', 'delete', 'reapply-storage-policy'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['sr'],
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope',
|
||||
'session_id',
|
||||
'storage_bucket_id',
|
||||
'bytes_up',
|
||||
'bytes_down',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'start_time',
|
||||
'end_time',
|
||||
'duration',
|
||||
'type',
|
||||
'state',
|
||||
'error_details',
|
||||
'mime_types',
|
||||
'endpoint',
|
||||
'connection_recordings',
|
||||
'create_time_values',
|
||||
'authorized_actions',
|
||||
'retain_until',
|
||||
'delete_after',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'managed-group',
|
||||
collection_actions: ['create', 'list'],
|
||||
id_actions: ['update', 'delete', 'read'],
|
||||
scopes: ['global', 'org'],
|
||||
id_prefixes: ['mgoidc', 'mgldap'],
|
||||
parent_type: 'auth-method',
|
||||
output_fields: [
|
||||
'id',
|
||||
'scope',
|
||||
'name',
|
||||
'description',
|
||||
'created_time',
|
||||
'updated_time',
|
||||
'version',
|
||||
'type',
|
||||
'auth_method_id',
|
||||
'attributes',
|
||||
'oidc_managed_group_attributes',
|
||||
'ldap_managed_group_attributes',
|
||||
'member_ids',
|
||||
'authorized_actions',
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -695,6 +695,7 @@ role:
|
||||
ids:
|
||||
empty-list: '"ids" field does not contain at least one id'
|
||||
wildcard-cannot-combine: Wildcard id "*" cannot be combined with other ids
|
||||
templates-cannot-combine: Template ids cannot be combined with each other
|
||||
duplicate-template: Duplicate id "{primary}" and "{secondary}"
|
||||
duplicate-id: Duplicate id "{id}"
|
||||
unknown-template: Unknown template "{id}"
|
||||
@@ -716,6 +717,12 @@ role:
|
||||
remove: Remove
|
||||
remove-comma: Remove comma
|
||||
add-noop: Add "no-op" action
|
||||
output-fields:
|
||||
empty-list: '"output_fields" field does not contain at least one field'
|
||||
wildcard-cannot-combine: Wildcard output field "*" cannot be combined with other fields
|
||||
duplicate-field: Duplicate output field "{field}"
|
||||
invalid-field: Invalid output field "{field}"
|
||||
invalid-field-for-type: Invalid output field "{field}" for type "{type}"
|
||||
editor:
|
||||
title: Grant Editor
|
||||
description: Add each new grant string on a different line
|
||||
|
||||
@@ -69,6 +69,7 @@ export const normalizeGrantsSchema = (grantsSchema) => {
|
||||
idActions,
|
||||
idPrefixes,
|
||||
parentType: resourceType.parent_type,
|
||||
outputFields: resourceType.output_fields ?? [],
|
||||
};
|
||||
|
||||
return resources;
|
||||
@@ -124,7 +125,7 @@ const getResourceTypesForId = (schema, id) => {
|
||||
.map(({ type }) => type);
|
||||
};
|
||||
|
||||
export const getCompatibleResourceTypeForIds = (schema, idsValue) => {
|
||||
const getCompatibleResourceTypeForIds = (schema, idsValue) => {
|
||||
const resourceTypesById = parseIds(idsValue).flatMap((id) =>
|
||||
getResourceTypesForId(schema, id),
|
||||
);
|
||||
@@ -152,7 +153,7 @@ const getIdActions = (schema, idsValue) => {
|
||||
return schema.resourcesByType[matchedType]?.idActions;
|
||||
};
|
||||
|
||||
export const getChildResourceActions = (schema, idsValue) => {
|
||||
const getChildResourceValues = (schema, idsValue, getValues) => {
|
||||
const parentType = getCompatibleResourceTypeForIds(schema, idsValue);
|
||||
|
||||
if (
|
||||
@@ -166,11 +167,25 @@ export const getChildResourceActions = (schema, idsValue) => {
|
||||
|
||||
return [
|
||||
...new Set(
|
||||
childTypes.flatMap((type) => schema.resourcesByType[type]?.actions ?? []),
|
||||
childTypes.flatMap((type) => getValues(schema.resourcesByType[type])),
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
const getChildResourceActions = (schema, idsValue) =>
|
||||
getChildResourceValues(
|
||||
schema,
|
||||
idsValue,
|
||||
(resource) => resource?.actions ?? [],
|
||||
);
|
||||
|
||||
const getChildResourceOutputFields = (schema, idsValue) =>
|
||||
getChildResourceValues(
|
||||
schema,
|
||||
idsValue,
|
||||
(resource) => resource?.outputFields ?? [],
|
||||
);
|
||||
|
||||
const getTypeOptions = (schema, idsValue) => {
|
||||
// If there is no ids value, we can only suggest top-level resource types that have collection actions
|
||||
if (!idsValue) {
|
||||
@@ -326,6 +341,37 @@ export const analyzeGrantString = (grantsSchema, grantString = '') => {
|
||||
};
|
||||
};
|
||||
|
||||
const getOutputFieldOptions = (schema, typeValue, idsValue) => {
|
||||
const selectedResource = schema.resourcesByType[typeValue];
|
||||
|
||||
if (typeValue && typeValue !== '*' && selectedResource) {
|
||||
return selectedResource.outputFields.length
|
||||
? withWildCard(selectedResource.outputFields)
|
||||
: ['*'];
|
||||
}
|
||||
|
||||
const hasSpecificIds = Boolean(idsValue) && !idsValue.includes('*');
|
||||
|
||||
// Pinned IDs with wildcard type
|
||||
if (typeValue === '*' && hasSpecificIds) {
|
||||
const childOutputFields = getChildResourceOutputFields(schema, idsValue);
|
||||
return childOutputFields.length ? withWildCard(childOutputFields) : ['*'];
|
||||
}
|
||||
|
||||
if (!typeValue && hasSpecificIds) {
|
||||
const matchedType = getCompatibleResourceTypeForIds(schema, idsValue);
|
||||
if (matchedType) {
|
||||
const resourceOutputFields =
|
||||
schema.resourcesByType[matchedType]?.outputFields ?? [];
|
||||
return resourceOutputFields.length
|
||||
? withWildCard(resourceOutputFields)
|
||||
: ['*'];
|
||||
}
|
||||
}
|
||||
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
const getIdLookupTypes = (schema, typeValue, enteredIds) => {
|
||||
if (enteredIds.length > 0) {
|
||||
const compatibleResourceType = getCompatibleResourceTypeForIds(
|
||||
@@ -411,15 +457,15 @@ async function grantCompletions(
|
||||
const idsValueMatch = beforeCursor.match(/ids=([\w*.{},\s-]*)$/);
|
||||
if (idsValueMatch) {
|
||||
const enteredIds = idsValueMatch[1].split(',');
|
||||
const partial = (enteredIds.pop() ?? '').trim();
|
||||
const partial = enteredIds.pop() ?? '';
|
||||
const hasEnteredIds = enteredIds.length > 0;
|
||||
const hasEnteredTemplateIds = enteredIds.some((id) => id.startsWith('{{'));
|
||||
|
||||
const staticOptions = filterByPrefix(['*', ...ID_TEMPLATES], partial)
|
||||
.filter((value) => !hasEnteredIds || value !== '*')
|
||||
.filter(
|
||||
(value) =>
|
||||
!ID_TEMPLATES.includes(value) ||
|
||||
(!typeValue && enteredIds.every((id) => ID_TEMPLATES.includes(id))),
|
||||
(!hasEnteredIds && !typeValue) || !ID_TEMPLATES.includes(value),
|
||||
)
|
||||
.filter((value) => !enteredIds.includes(value))
|
||||
.map((value) => ({
|
||||
@@ -438,7 +484,9 @@ async function grantCompletions(
|
||||
);
|
||||
const from = context.pos - partial.length;
|
||||
|
||||
const ids = await idLookup(partial, compatibleResourceTypes);
|
||||
const ids = hasEnteredTemplateIds
|
||||
? []
|
||||
: await idLookup(partial, compatibleResourceTypes);
|
||||
const idOptions = ids
|
||||
.filter(({ id }) => !enteredIds.includes(id))
|
||||
.map(({ id, name }) => ({
|
||||
@@ -506,15 +554,23 @@ async function grantCompletions(
|
||||
const partial = enteredOutputFields.pop() ?? '';
|
||||
const hasEnteredOutputFields = enteredOutputFields.length > 0;
|
||||
|
||||
const options = filterByPrefix(
|
||||
getOutputFieldOptions(schema, typeValue, idsValue),
|
||||
partial,
|
||||
)
|
||||
.filter((value) => !hasEnteredOutputFields || value !== '*')
|
||||
.filter((value) => !enteredOutputFields.includes(value))
|
||||
.map((value) => ({
|
||||
label: value,
|
||||
type: 'constant',
|
||||
info: value === '*' ? translate('all-fields') : '',
|
||||
}));
|
||||
|
||||
return {
|
||||
from: context.pos - partial.length,
|
||||
options: filterByPrefix(['*'], partial)
|
||||
.filter((value) => !hasEnteredOutputFields || value !== '*')
|
||||
.map((value) => ({
|
||||
label: value,
|
||||
type: 'constant',
|
||||
info: translate('all-fields'),
|
||||
})),
|
||||
options: options.length
|
||||
? options
|
||||
: [getNoSuggestionsOption(translate('no-suggestions'))],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ const normalizeGrantsSchema = (grantsSchema) => {
|
||||
idActions,
|
||||
idPrefixes,
|
||||
parentType: resourceType.parent_type,
|
||||
outputFields: resourceType.output_fields ?? [],
|
||||
};
|
||||
|
||||
return resources;
|
||||
@@ -415,6 +416,44 @@ const grantLinter = (context, schema, translate) => {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate output_fields field
|
||||
if (parsedFields.output_fields) {
|
||||
const pos = fieldPositions.output_fields;
|
||||
const outputFieldList = validateListField(
|
||||
parsedFields.output_fields,
|
||||
pos,
|
||||
diagnostics,
|
||||
translate,
|
||||
);
|
||||
if (outputFieldList.length === 0) {
|
||||
diagnostics.push({
|
||||
from: pos.valueStart,
|
||||
to: pos.valueEnd,
|
||||
severity: 'error',
|
||||
message: translate('output-fields.empty-list'),
|
||||
});
|
||||
} else if (outputFieldList.includes('*') && outputFieldList.length > 1) {
|
||||
const wildcardIndex =
|
||||
pos.valueStart + parsedFields.output_fields.indexOf('*');
|
||||
diagnostics.push({
|
||||
from: wildcardIndex,
|
||||
to: wildcardIndex + 1,
|
||||
severity: 'error',
|
||||
message: translate('output-fields.wildcard-cannot-combine'),
|
||||
actions: [createDeleteTextAction(translate('editor.remove'))],
|
||||
});
|
||||
} else {
|
||||
validateOutputFieldsField(
|
||||
schema,
|
||||
diagnostics,
|
||||
outputFieldList,
|
||||
validatedFields,
|
||||
pos,
|
||||
translate,
|
||||
);
|
||||
}
|
||||
}
|
||||
currentPos += line.length + 1; // +1 for newline
|
||||
});
|
||||
|
||||
@@ -606,7 +645,13 @@ const validateIdsField = (
|
||||
}
|
||||
if (seenIdType === 'template') {
|
||||
if (idsList.length > 1) {
|
||||
seenIdType = 'template-multiple';
|
||||
diagnostics.push({
|
||||
from: idsPos.valueStart,
|
||||
to: idsPos.valueEnd,
|
||||
severity: 'error',
|
||||
message: translate('ids.templates-cannot-combine'),
|
||||
});
|
||||
return;
|
||||
} else if (
|
||||
idsList[0] === '{{.Account.Id}}' ||
|
||||
idsList[0] === '{{account.id}}'
|
||||
@@ -621,12 +666,6 @@ const validateIdsField = (
|
||||
|
||||
const getIdActionsForResourceType = (schema, resourceType) => {
|
||||
switch (resourceType) {
|
||||
case 'template-multiple':
|
||||
return [
|
||||
'*',
|
||||
...schema.resourcesByType['account'].idActions,
|
||||
...schema.resourcesByType['user'].idActions,
|
||||
];
|
||||
case 'account-template':
|
||||
return ['*', ...schema.resourcesByType['account'].idActions];
|
||||
case 'user-template':
|
||||
@@ -761,6 +800,87 @@ const validateActionsField = (
|
||||
}
|
||||
};
|
||||
|
||||
const validateOutputFieldsField = (
|
||||
schema,
|
||||
diagnostics,
|
||||
outputFieldList,
|
||||
validatedFields,
|
||||
pos,
|
||||
translate,
|
||||
) => {
|
||||
let validOutputFields = ['*'];
|
||||
let errorMessage = (field) =>
|
||||
translate('output-fields.invalid-field', { field });
|
||||
|
||||
if (validatedFields.type?.value && !validatedFields.type.wildcard) {
|
||||
const resourceOutputFields =
|
||||
schema.resourcesByType[validatedFields.type.value]?.outputFields ?? [];
|
||||
validOutputFields = ['*', ...resourceOutputFields];
|
||||
errorMessage = (field) =>
|
||||
translate('output-fields.invalid-field-for-type', {
|
||||
field,
|
||||
type: validatedFields.type.value,
|
||||
});
|
||||
} else if (validatedFields.type?.wildcard && validatedFields.ids?.knownType) {
|
||||
// Pinned IDs with wildcard type: validate against union of child types' output fields
|
||||
const parentType = validatedFields.ids.knownType;
|
||||
const childTypes = schema.childResourceTypesByParentType[parentType] ?? [];
|
||||
const childOutputFields = [
|
||||
...new Set(
|
||||
childTypes.flatMap(
|
||||
(type) => schema.resourcesByType[type]?.outputFields ?? [],
|
||||
),
|
||||
),
|
||||
];
|
||||
validOutputFields = ['*', ...childOutputFields];
|
||||
errorMessage = (field) =>
|
||||
translate('output-fields.invalid-field', { field });
|
||||
} else if (validatedFields.ids?.knownType && !validatedFields.type) {
|
||||
const knownType = validatedFields.ids.knownType;
|
||||
const TEMPLATE_RESOURCE_TYPES = {
|
||||
'account-template': 'account',
|
||||
'user-template': 'user',
|
||||
};
|
||||
const resolvedType = TEMPLATE_RESOURCE_TYPES[knownType] ?? knownType;
|
||||
const resourceOutputFields =
|
||||
schema.resourcesByType[resolvedType]?.outputFields ?? [];
|
||||
validOutputFields = ['*', ...resourceOutputFields];
|
||||
errorMessage = (field) =>
|
||||
translate('output-fields.invalid-field-for-type', {
|
||||
field,
|
||||
type: resolvedType,
|
||||
});
|
||||
}
|
||||
|
||||
let segmentStart = pos.valueStart;
|
||||
for (const [i, field] of outputFieldList.entries()) {
|
||||
let segmentEnd = segmentStart + field.length;
|
||||
if (i < outputFieldList.length - 1) {
|
||||
segmentEnd += 1;
|
||||
}
|
||||
if (outputFieldList.slice(i + 1).includes(field)) {
|
||||
diagnostics.push({
|
||||
from: segmentStart,
|
||||
to: segmentEnd,
|
||||
severity: 'error',
|
||||
message: translate('output-fields.duplicate-field', { field }),
|
||||
actions: [createDeleteTextAction(translate('editor.remove'))],
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!validOutputFields.includes(field)) {
|
||||
diagnostics.push({
|
||||
from: pos.valueStart,
|
||||
to: pos.valueEnd,
|
||||
severity: 'error',
|
||||
message: errorMessage(field),
|
||||
});
|
||||
return;
|
||||
}
|
||||
segmentStart += field.length + 1;
|
||||
}
|
||||
};
|
||||
|
||||
export const createGrantLinter = (grantsSchema, translate) => {
|
||||
const schema = normalizeGrantsSchema(grantsSchema);
|
||||
return (context) => grantLinter(context, schema, translate);
|
||||
|
||||
@@ -11,9 +11,6 @@ import { setupMirage } from 'admin/tests/helpers/mirage';
|
||||
import {
|
||||
createGrantCompletionSource,
|
||||
analyzeGrantString,
|
||||
getChildResourceActions,
|
||||
getCompatibleResourceTypeForIds,
|
||||
normalizeGrantsSchema,
|
||||
} from 'admin/utils/grant-completions';
|
||||
|
||||
const createCompletionContext = (lineText) => ({
|
||||
@@ -185,7 +182,10 @@ module('Unit | Utils | grant-completions', function (hooks) {
|
||||
label === 'NO_SUGGESTIONS' ? this.translate('no-suggestions') : label,
|
||||
);
|
||||
|
||||
assert.deepEqual(getLabels(completionResult), resolvedLabels);
|
||||
assert.deepEqual(
|
||||
[...getLabels(completionResult)].sort(),
|
||||
[...resolvedLabels].sort(),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -389,7 +389,10 @@ module('Unit | Utils | grant-completions', function (hooks) {
|
||||
label === 'NO_SUGGESTIONS' ? this.translate('no-suggestions') : label,
|
||||
);
|
||||
|
||||
assert.deepEqual(getLabels(completionResult), resolvedLabels);
|
||||
assert.deepEqual(
|
||||
[...getLabels(completionResult)].sort(),
|
||||
[...resolvedLabels].sort(),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -404,21 +407,129 @@ module('Unit | Utils | grant-completions', function (hooks) {
|
||||
lineText: 'ids=',
|
||||
expectedLabels: ['*', '{{.User.Id}}', '{{.Account.Id}}'],
|
||||
},
|
||||
'filter wildcard once an id is already entered': {
|
||||
lineText: 'ids={{.User.Id}},',
|
||||
expectedLabels: ['{{.Account.Id}}'],
|
||||
'filter out wildcard and templates once an id is already entered': {
|
||||
lineText: 'ids=h_123,',
|
||||
expectedLabels: ['NO_SUGGESTIONS'],
|
||||
},
|
||||
'filter already-entered template ids when completing a partial': {
|
||||
lineText: 'ids={{.User.Id}},{{',
|
||||
expectedLabels: ['{{.Account.Id}}'],
|
||||
'filter out wildcard and templates once a template is already entered': {
|
||||
lineText: 'ids={{.User.Id}},',
|
||||
expectedLabels: ['NO_SUGGESTIONS'],
|
||||
},
|
||||
},
|
||||
async function (assert, { lineText, expectedLabels }) {
|
||||
const completionResult = await this.grantCompletionSource(
|
||||
createCompletionContext(lineText),
|
||||
);
|
||||
const resolvedLabels = expectedLabels.map((label) =>
|
||||
label === 'NO_SUGGESTIONS' ? this.translate('no-suggestions') : label,
|
||||
);
|
||||
|
||||
assert.deepEqual(getLabels(completionResult), expectedLabels);
|
||||
assert.deepEqual(getLabels(completionResult), resolvedLabels);
|
||||
},
|
||||
);
|
||||
|
||||
test.each(
|
||||
'output_fields suggestions',
|
||||
{
|
||||
'with a specific type suggests wildcard and that type output fields': {
|
||||
lineText: 'type=target;output_fields=',
|
||||
getExpected: (schema) => {
|
||||
const { output_fields } = schema.resource_types.find(
|
||||
(r) => r.type === 'target',
|
||||
);
|
||||
return ['*', ...output_fields];
|
||||
},
|
||||
},
|
||||
'with specific ids infers the type and suggests its output fields': {
|
||||
lineText: 'ids=ttcp_123;output_fields=',
|
||||
getExpected: (schema) => {
|
||||
const { output_fields } = schema.resource_types.find(
|
||||
(r) => r.type === 'target',
|
||||
);
|
||||
return ['*', ...output_fields];
|
||||
},
|
||||
},
|
||||
'with a user template id suggests user output fields': {
|
||||
lineText: 'ids={{.User.Id}};output_fields=',
|
||||
getExpected: (schema) => {
|
||||
const { output_fields } = schema.resource_types.find(
|
||||
(r) => r.type === 'user',
|
||||
);
|
||||
return ['*', ...output_fields];
|
||||
},
|
||||
},
|
||||
'with the user.id template suggests user output fields': {
|
||||
lineText: 'ids={{user.id}};output_fields=',
|
||||
getExpected: (schema) => {
|
||||
const { output_fields } = schema.resource_types.find(
|
||||
(r) => r.type === 'user',
|
||||
);
|
||||
return ['*', ...output_fields];
|
||||
},
|
||||
},
|
||||
'with an account template id suggests account output fields': {
|
||||
lineText: 'ids={{.Account.Id}};output_fields=',
|
||||
getExpected: (schema) => {
|
||||
const { output_fields } = schema.resource_types.find(
|
||||
(r) => r.type === 'account',
|
||||
);
|
||||
return ['*', ...output_fields];
|
||||
},
|
||||
},
|
||||
'with the account.id template suggests account output fields': {
|
||||
lineText: 'ids={{account.id}};output_fields=',
|
||||
getExpected: (schema) => {
|
||||
const { output_fields } = schema.resource_types.find(
|
||||
(r) => r.type === 'account',
|
||||
);
|
||||
return ['*', ...output_fields];
|
||||
},
|
||||
},
|
||||
'mixed user and account template ids suggests only wildcard': {
|
||||
lineText: 'ids={{.User.Id}},{{.Account.Id}};output_fields=',
|
||||
getExpected: () => ['*'],
|
||||
},
|
||||
'pinned parent id with wildcard type suggests union of child output fields':
|
||||
{
|
||||
lineText: 'ids=hcst_123;type=*;output_fields=',
|
||||
getExpected: (schema) => {
|
||||
const childFields = schema.resource_types
|
||||
.filter((r) => r.parent_type === 'host-catalog')
|
||||
.flatMap((r) => r.output_fields);
|
||||
return ['*', ...new Set(childFields)];
|
||||
},
|
||||
},
|
||||
'excludes already entered output fields': {
|
||||
lineText: 'type=target;output_fields=id,',
|
||||
getExpected: (schema) => {
|
||||
const { output_fields } = schema.resource_types.find(
|
||||
(r) => r.type === 'target',
|
||||
);
|
||||
return output_fields.filter((f) => f !== 'id');
|
||||
},
|
||||
},
|
||||
'partial prefix filters output fields': {
|
||||
lineText: 'type=target;output_fields=sc',
|
||||
getExpected: (schema) => {
|
||||
const { output_fields } = schema.resource_types.find(
|
||||
(r) => r.type === 'target',
|
||||
);
|
||||
return output_fields.filter((f) => f.startsWith('sc'));
|
||||
},
|
||||
},
|
||||
'without type or ids suggests only wildcard': {
|
||||
lineText: 'output_fields=',
|
||||
getExpected: () => ['*'],
|
||||
},
|
||||
},
|
||||
async function (assert, { lineText, getExpected }) {
|
||||
const completionResult = await this.grantCompletionSource(
|
||||
createCompletionContext(lineText),
|
||||
);
|
||||
const labels = getLabels(completionResult);
|
||||
const expected = getExpected(this.grantsSchemaData);
|
||||
|
||||
assert.deepEqual([...labels].sort(), [...expected].sort());
|
||||
},
|
||||
);
|
||||
|
||||
@@ -484,42 +595,6 @@ module('Unit | Utils | grant-completions', function (hooks) {
|
||||
assert.deepEqual(result.actions, []);
|
||||
});
|
||||
|
||||
test('getCompatibleResourceTypeForIds returns the matched type for a known id prefix', function (assert) {
|
||||
const schema = normalizeGrantsSchema(this.grantsSchemaData);
|
||||
|
||||
assert.strictEqual(
|
||||
getCompatibleResourceTypeForIds(schema, 'hs_123'),
|
||||
'host-set',
|
||||
);
|
||||
});
|
||||
|
||||
test('getCompatibleResourceTypeForIds returns null for an unknown id prefix', function (assert) {
|
||||
const schema = normalizeGrantsSchema(this.grantsSchemaData);
|
||||
|
||||
assert.strictEqual(
|
||||
getCompatibleResourceTypeForIds(schema, 'bad_123'),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
test('getCompatibleResourceTypeForIds returns null for mixed resource type ids', function (assert) {
|
||||
const schema = normalizeGrantsSchema(this.grantsSchemaData);
|
||||
|
||||
assert.strictEqual(
|
||||
getCompatibleResourceTypeForIds(schema, 'hs_123,s_123'),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
test('getCompatibleResourceTypeForIds returns the type when all ids share the same resource type', function (assert) {
|
||||
const schema = normalizeGrantsSchema(this.grantsSchemaData);
|
||||
|
||||
assert.strictEqual(
|
||||
getCompatibleResourceTypeForIds(schema, 'hs_123,hs_456'),
|
||||
'host-set',
|
||||
);
|
||||
});
|
||||
|
||||
test('analyzeGrantString.actions returns the same actions as the completion source uses', function (assert) {
|
||||
const { actions } = analyzeGrantString(
|
||||
this.grantsSchemaData,
|
||||
@@ -583,21 +658,6 @@ module('Unit | Utils | grant-completions', function (hooks) {
|
||||
assert.true(result.hasInvalidType);
|
||||
});
|
||||
|
||||
test('getChildResourceActions returns the union of child resource actions for a pinned parent id', function (assert) {
|
||||
const schema = normalizeGrantsSchema(this.grantsSchemaData);
|
||||
|
||||
assert.deepEqual(getChildResourceActions(schema, 'hcst_1234567890'), [
|
||||
'create',
|
||||
'list',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
'add-hosts',
|
||||
'set-hosts',
|
||||
'remove-hosts',
|
||||
]);
|
||||
});
|
||||
|
||||
test.each(
|
||||
'analyzeGrantString.hasInvalidType',
|
||||
{
|
||||
@@ -650,6 +710,10 @@ module('Unit | Utils | grant-completions', function (hooks) {
|
||||
grantString: 'ids={{.User.Id}}',
|
||||
expected: false,
|
||||
},
|
||||
'returns false for multiple ids sharing the same resource type': {
|
||||
grantString: 'ids=hs_123,hs_456',
|
||||
expected: false,
|
||||
},
|
||||
},
|
||||
function (assert, { grantString, expected }) {
|
||||
const { hasInvalidIds } = analyzeGrantString(
|
||||
@@ -692,6 +756,7 @@ module('Unit | Utils | grant-completions', function (hooks) {
|
||||
assert.strictEqual(hasInvalidPinnedIdTypeCombination, expected);
|
||||
},
|
||||
);
|
||||
|
||||
module('id auto completion', function () {
|
||||
test('idLookup results are included alongside static options', async function (assert) {
|
||||
const source = createGrantCompletionSource(
|
||||
@@ -847,9 +912,9 @@ module('Unit | Utils | grant-completions', function (hooks) {
|
||||
this.translate,
|
||||
async (partial, compatibleResourceTypes) => {
|
||||
assert.deepEqual(compatibleResourceTypes, [
|
||||
'credential-store',
|
||||
'auth-method',
|
||||
'host-catalog',
|
||||
'credential-store',
|
||||
]);
|
||||
return [];
|
||||
},
|
||||
@@ -879,6 +944,19 @@ module('Unit | Utils | grant-completions', function (hooks) {
|
||||
assert.true(getLabels(result).includes('hcst_parentid'));
|
||||
});
|
||||
|
||||
test('no ID suggestions are shown when a template id is already entered', async function (assert) {
|
||||
const source = createGrantCompletionSource(
|
||||
this.grantsSchemaData,
|
||||
this.translate,
|
||||
async () => [{ id: 'u_abc123', name: 'My User' }],
|
||||
() => false,
|
||||
);
|
||||
|
||||
const result = await source(createCompletionContext('ids={{.User.Id}},'));
|
||||
|
||||
assert.deepEqual(getLabels(result), [this.translate('no-suggestions')]);
|
||||
});
|
||||
|
||||
test('already-entered IDs are not offered again', async function (assert) {
|
||||
const source = createGrantCompletionSource(
|
||||
this.grantsSchemaData,
|
||||
|
||||
@@ -222,7 +222,10 @@ module('Unit | Utility | grant-linter', function (hooks) {
|
||||
['ids=*;type=*;actions=read'], // valid wildcard id
|
||||
['ids=hcst_123,hcplg_456;type=host-set;actions=read'], // valid pinned ids for host-set resource type
|
||||
['ids={{.Account.Id}};actions=read'], // valid template id when type is not specified
|
||||
['ids={{.Account.Id}},{{.User.Id}};actions=read'], // valid template ids when type is not specified
|
||||
[
|
||||
'ids={{.Account.Id}},{{.User.Id}};actions=read',
|
||||
'Template ids cannot be combined with each other',
|
||||
], // mixed template ids is not allowed
|
||||
['ids=csst_1234,csvlt_5678;actions=*'], // valid id prefixes with same type
|
||||
['ids=amldap_123,amoidc_456;type=*;actions=read,list'], // valid ids with wildcard type
|
||||
],
|
||||
@@ -236,4 +239,72 @@ module('Unit | Utility | grant-linter', function (hooks) {
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test.each(
|
||||
'diagnostics for output_fields field values',
|
||||
[
|
||||
['type=target;output_fields=,', 'Invalid syntax'],
|
||||
[
|
||||
'type=target;output_fields=*,id',
|
||||
'Wildcard output field "*" cannot be combined with other fields',
|
||||
],
|
||||
['type=target;output_fields=id,id', 'Duplicate output field "id"'],
|
||||
[
|
||||
'type=target;output_fields=invalid_field',
|
||||
'Invalid output field "invalid_field" for type "target"',
|
||||
],
|
||||
[
|
||||
'ids=ttcp_123;output_fields=member_ids',
|
||||
'Invalid output field "member_ids" for type "target"',
|
||||
],
|
||||
['ids=hcst_123;type=*;actions=read;output_fields=host_catalog_id'], // valid: child type (host) output field
|
||||
['ids=hcst_123;type=*;actions=read;output_fields=host_set_ids'], // valid: child type (host) output field
|
||||
[
|
||||
'ids=hcst_123;type=*;actions=read;output_fields=session_max_seconds',
|
||||
'Invalid output field "session_max_seconds"',
|
||||
], // invalid: not a host or host-set field
|
||||
[
|
||||
'ids=*;type=*;actions=read;output_fields=id',
|
||||
'Invalid output field "id"',
|
||||
], // no specific type context, specific field is invalid
|
||||
['type=target;output_fields=id,name'], // valid output fields
|
||||
['type=target;output_fields=*'], // valid wildcard
|
||||
['ids=ttcp_123;output_fields=scope_id'], // valid inferred type
|
||||
['ids={{.User.Id}};output_fields=email'], // valid user template output field
|
||||
['ids={{user.id}};output_fields=email'], // valid user template alias output field
|
||||
['ids={{.Account.Id}};output_fields=auth_method_id'], // valid account template output field
|
||||
['ids={{account.id}};output_fields=auth_method_id'], // valid account template alias output field
|
||||
[
|
||||
'ids={{.User.Id}};output_fields=auth_method_id',
|
||||
'Invalid output field "auth_method_id" for type "user"',
|
||||
], // account field is invalid for user template
|
||||
[
|
||||
'ids={{.Account.Id}};output_fields=email',
|
||||
'Invalid output field "email" for type "account"',
|
||||
], // user field is invalid for account template
|
||||
[
|
||||
'ids={{.User.Id}},{{.Account.Id}};output_fields=email',
|
||||
'Template ids cannot be combined with each other',
|
||||
], // ids error fires first; mixed templates are disallowed
|
||||
[
|
||||
'ids={{.User.Id}},{{.Account.Id}};output_fields=auth_method_id',
|
||||
'Template ids cannot be combined with each other',
|
||||
], // ids error fires first; mixed templates are disallowed
|
||||
[
|
||||
'ids={{.User.Id}},{{.Account.Id}};output_fields=session_max_seconds',
|
||||
'Template ids cannot be combined with each other',
|
||||
], // ids error fires first; mixed templates are disallowed
|
||||
['type=target;output_fields=id,scope_id,name'], // valid multiple output fields
|
||||
['ids=*;type=*;actions=read;output_fields=*'], // wildcard output fields with wildcard type is valid
|
||||
],
|
||||
function (assert, [grantString, errorMsg]) {
|
||||
const diagnostics = this.grantLinter(createContext(grantString));
|
||||
|
||||
if (errorMsg) {
|
||||
assert.strictEqual(diagnostics[0].message, errorMsg);
|
||||
} else {
|
||||
assert.strictEqual(diagnostics.length, 0);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user