diff --git a/CHANGELOG.md b/CHANGELOG.md index 9522b58..fb92829 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ This changelog is following the recommended format by [keepachangelog](https://k ## [Unreleased] +### Added + +- Add searching and viewing identities by [@henrique-quintino-sp](https://github.com/henrique-quintino-sp) (cf. [#74](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/pull/74)) +- Add attribute sync, process and delete command on identities by [@henrique-quintino-sp](https://github.com/henrique-quintino-sp) (cf. [#74](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/pull/74)) + ## [1.2.0] - 2024-04-09 ### Added diff --git a/README.md b/README.md index 92113c8..203bb26 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The SailPoint Identity Security Cloud extension makes it easy to: - View, edit, create, delete, export, import roles - View, edit, create, delete, export, import forms - View, edit, create search attribute config -- View, edit identity attributes +- View, trigger attribute sync or process, delete identities ## Installation @@ -302,6 +302,9 @@ The patterns defined above use the following tokens: ## Release Notes +- Add searching and viewing identities by [@henrique-quintino-sp](https://github.com/henrique-quintino-sp) (cf. [#74](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/pull/74)) +- Add attribute sync, process and delete command on identities by [@henrique-quintino-sp](https://github.com/henrique-quintino-sp) (cf. [#74](https://github.com/yannick-beot-sp/vscode-sailpoint-identitynow/pull/74)) + ## 1.2.0 - Add new command to edit connector rule (Edit script) diff --git a/package.json b/package.json index 3e4a18d..77040bf 100644 --- a/package.json +++ b/package.json @@ -489,6 +489,27 @@ "command": "vscode-sailpoint-identitynow.new-attribute-search.view", "title": "New Search Attribute", "icon": "$(add)" + }, + { + "command": "vscode-sailpoint-identitynow.identities.icon-search", + "title": "Search Identity", + "icon": "$(search)" + }, + { + "command": "vscode-sailpoint-identitynow.identities.search", + "title": "Search identities..." + }, + { + "command": "vscode-sailpoint-identitynow.identities.delete", + "title": "Delete" + }, + { + "command": "vscode-sailpoint-identitynow.identities.att-sync", + "title": "Synchronize attributes" + }, + { + "command": "vscode-sailpoint-identitynow.identities.process", + "title": "Process" } ], "menus": { @@ -765,6 +786,26 @@ "command": "vscode-sailpoint-identitynow.new-attribute-search.view", "when": "never" }, + { + "command": "vscode-sailpoint-identitynow.identities.search", + "when": "never" + }, + { + "command": "vscode-sailpoint-identitynow.identities.icon-search", + "when": "never" + }, + { + "command": "vscode-sailpoint-identitynow.identities.delete", + "when": "never" + }, + { + "command": "vscode-sailpoint-identitynow.identities.att-sync", + "when": "never" + }, + { + "command": "vscode-sailpoint-identitynow.identities.process", + "when": "never" + }, { "command": "vscode-sailpoint-identitynow.connector-rule.export-script.view", "when": "never" @@ -1105,6 +1146,27 @@ "command": "vscode-sailpoint-identitynow.new-attribute-search.view", "when": "view == vscode-sailpoint-identitynow.view && viewItem == search-attributes", "group": "inline@2" + }, + { + "command": "vscode-sailpoint-identitynow.identities.search", + "when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^identities/" + }, + { + "command": "vscode-sailpoint-identitynow.identities.icon-search", + "when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^identities/", + "group": "inline@1" + }, + { + "command": "vscode-sailpoint-identitynow.identities.delete", + "when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^identity/" + }, + { + "command": "vscode-sailpoint-identitynow.identities.att-sync", + "when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^identity/" + }, + { + "command": "vscode-sailpoint-identitynow.identities.process", + "when": "view == vscode-sailpoint-identitynow.view && viewItem =~ /^identity/" } ], "editor/context": [ @@ -1187,6 +1249,10 @@ { "fileMatch": "idn://**/access-request-config/**/*", "url": "./schemas/access-request-config.schemas.json" + }, + { + "fileMatch": "idn://**/identities/**/*", + "url": "./schemas/identity.schemas.json" } ], "configuration": { diff --git a/schemas/identity.schemas.json b/schemas/identity.schemas.json new file mode 100644 index 0000000..aea4a99 --- /dev/null +++ b/schemas/identity.schemas.json @@ -0,0 +1,908 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://developer.sailpoint.com/schemas/identity.json", + "title": "Identity", + "description": "Schema of an identity", + "type": "object", + "properties": { + "displayName": { + "type": "string", + "example": "Carol.Adams", + "description": "Identity's display name." + }, + "firstName": { + "type": "string", + "description": "Identity's first name.", + "example": "Carol" + }, + "lastName": { + "type": "string", + "description": "Identity's last name.", + "example": "Adams" + }, + "email": { + "type": "string", + "description": "Identity's primary email address.", + "example": "Carol.Adams@sailpointdemo.com" + }, + "created": { + "type": "string", + "description": "ISO-8601 date-time referring to the time when the object was created.", + "nullable": true, + "format": "date-time", + "example": "2018-06-25T20:22:28.104Z" + }, + "modified": { + "type": "string", + "description": "ISO-8601 date-time referring to the time when the object was last modified.", + "nullable": true, + "format": "date-time", + "example": "2018-06-25T20:22:28.104Z" + }, + "phone": { + "type": "string", + "description": "Identity's phone number.", + "example": "+1 440-527-3672" + }, + "synced": { + "type": "string", + "description": "ISO-8601 date-time referring to the date-time when object was queued to be synced into search database for use in the search API. \nThis date-time changes anytime there is an update to the object, which triggers a synchronization event being sent to the search database. \nThere may be some delay between the `synced` time and the time when the updated data is actually available in the search API. " + }, + "inactive": { + "type": "boolean", + "description": "Indicates whether the identity is inactive.", + "default": false, + "example": false + }, + "protected": { + "type": "boolean", + "description": "Indicates whether the identity is protected.", + "default": false, + "example": false + }, + "status": { + "type": "string", + "description": "Identity's status in SailPoint.", + "example": "UNREGISTERED" + }, + "employeeNumber": { + "type": "string", + "description": "Identity's employee number.", + "example": "1a2a3d4e" + }, + "manager": { + "type": "object", + "description": "Identity's manager.", + "nullable": true, + "properties": { + "id": { + "type": "string", + "description": "ID of identity's manager.", + "example": "2c9180867dfe694b017e208e27c05799" + }, + "name": { + "type": "string", + "description": "Name of identity's manager.", + "example": "Amanda.Ross" + }, + "displayName": { + "type": "string", + "description": "Display name of identity's manager.", + "example": "Amanda.Ross" + } + } + }, + "isManager": { + "type": "boolean", + "description": "Indicates whether the identity is a manager of other identities.", + "example": false + }, + "identityProfile": { + "type": "object", + "description": "Identity's identity profile.", + "properties": { + "id": { + "type": "string", + "description": "Identity profile's ID.", + "example": "3bc8ad26b8664945866b31339d1ff7d2" + }, + "name": { + "type": "string", + "description": "Identity profile's name.", + "example": "HR Employees" + } + } + }, + "source": { + "type": "object", + "description": "Identity's source.", + "properties": { + "id": { + "type": "string", + "description": "ID of identity's source.", + "example": "2c91808b6e9e6fb8016eec1a2b6f7b5f" + }, + "name": { + "type": "string", + "description": "Display name of identity's source.", + "example": "ODS-HR-Employees" + } + } + }, + "attributes": { + "type": "object", + "description": "Map or dictionary of key/value pairs.", + "additionalProperties": true, + "example": { + "country": "US", + "firstname": "Carol", + "cloudStatus": "UNREGISTERED" + } + }, + "processingState": { + "type": "string", + "description": "Identity's processing state.", + "nullable": true, + "example": null + }, + "processingDetails": { + "description": "Identity's processing details.", + "nullable": true, + "type": "object", + "properties": { + "date": { + "type": "string", + "nullable": true, + "format": "date-time", + "example": "2018-06-25T20:22:28.104Z", + "description": "A date-time in ISO-8601 format" + }, + "stage": { + "type": "string", + "example": "In Process" + }, + "retryCount": { + "type": "integer", + "example": 0, + "format": "int32" + }, + "stackTrace": { + "type": "string", + "example": "" + }, + "message": { + "type": "string", + "example": "" + } + } + }, + "accounts": { + "type": "array", + "description": "List of accounts associated with the identity.", + "items": { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + { + "type": "object", + "properties": { + "accountId": { + "type": "string", + "description": "Account ID.", + "example": "John.Doe" + }, + "source": { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "example": "Delimited File", + "description": "Type of source returned." + } + } + } + ] + }, + "disabled": { + "type": "boolean", + "description": "Indicates whether the account is disabled.", + "default": false, + "example": false + }, + "locked": { + "type": "boolean", + "description": "Indicates whether the account is locked.", + "default": false, + "example": false + }, + "privileged": { + "type": "boolean", + "description": "Indicates whether the account is privileged.", + "default": false, + "example": false + }, + "manuallyCorrelated": { + "type": "boolean", + "description": "Indicates whether the account has been manually correlated to an identity.", + "default": false, + "example": false + }, + "passwordLastSet": { + "type": "string", + "nullable": true, + "format": "date-time", + "example": "2018-06-25T20:22:28.104Z", + "description": "A date-time in ISO-8601 format" + }, + "entitlementAttributes": { + "type": "object", + "nullable": true, + "description": "Map or dictionary of key/value pairs.", + "additionalProperties": true, + "example": { + "moderator": true, + "admin": true, + "trust_level": "4" + } + }, + "created": { + "type": "string", + "description": "ISO-8601 date-time referring to the time when the object was created.", + "nullable": true, + "format": "date-time", + "example": "2018-06-25T20:22:28.104Z" + } + } + } + ] + } + }, + "accountCount": { + "type": "integer", + "description": "Number of accounts associated with the identity.", + "format": "int32", + "example": 3 + }, + "apps": { + "type": "array", + "description": "List of applications the identity has access to.", + "items": { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + { + "type": "object", + "properties": { + "source": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + "account": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The SailPoint generated unique ID", + "example": "2c9180837dfe6949017e21f3d8cd6d49" + }, + "accountId": { + "type": "string", + "description": "The account ID generated by the source", + "example": "CN=Carol Adams,OU=Austin,OU=Americas,OU=Demo,DC=seri,DC=sailpointdemo,DC=com" + } + } + } + } + } + ] + } + }, + "appCount": { + "type": "integer", + "format": "int32", + "description": "Number of applications the identity has access to.", + "example": 2 + }, + "access": { + "type": "array", + "description": "List of access items assigned to the identity.", + "items": { + "discriminator": { + "propertyName": "type", + "mapping": { + "ACCESS_PROFILE": "../access/AccessProfileSummary.yaml", + "ENTITLEMENT": "../access/AccessProfileEntitlement.yaml", + "ROLE": "../access/AccessProfileRole.yaml" + } + }, + "oneOf": [ + { + "description": "This is a summary representation of an access profile.", + "allOf": [ + { + "allOf": [ + { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "example": "John Q. Doe" + } + } + } + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "ACCOUNT_CORRELATION_CONFIG", + "ACCESS_PROFILE", + "ACCESS_REQUEST_APPROVAL", + "ACCOUNT", + "APPLICATION", + "CAMPAIGN", + "CAMPAIGN_FILTER", + "CERTIFICATION", + "CLUSTER", + "CONNECTOR_SCHEMA", + "ENTITLEMENT", + "GOVERNANCE_GROUP", + "IDENTITY", + "IDENTITY_PROFILE", + "IDENTITY_REQUEST", + "LIFECYCLE_STATE", + "PASSWORD_POLICY", + "ROLE", + "RULE", + "SOD_POLICY", + "SOURCE", + "TAG", + "TAG_CATEGORY", + "TASK_RESULT", + "REPORT_RESULT", + "SOD_VIOLATION", + "ACCOUNT_ACTIVITY", + "WORKGROUP" + ], + "description": "An enumeration of the types of DTOs supported within the IdentityNow infrastructure.", + "example": "IDENTITY" + }, + "description": { + "type": "string", + "nullable": true, + "example": null + } + } + } + ] + }, + { + "type": "object", + "properties": { + "source": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + "owner": { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "example": "John Q. Doe" + } + } + } + ] + }, + "revocable": { + "type": "boolean", + "example": true + } + } + } + ] + }, + { + "description": "EntitlementReference", + "allOf": [ + { + "allOf": [ + { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "example": "John Q. Doe" + } + } + } + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "ACCOUNT_CORRELATION_CONFIG", + "ACCESS_PROFILE", + "ACCESS_REQUEST_APPROVAL", + "ACCOUNT", + "APPLICATION", + "CAMPAIGN", + "CAMPAIGN_FILTER", + "CERTIFICATION", + "CLUSTER", + "CONNECTOR_SCHEMA", + "ENTITLEMENT", + "GOVERNANCE_GROUP", + "IDENTITY", + "IDENTITY_PROFILE", + "IDENTITY_REQUEST", + "LIFECYCLE_STATE", + "PASSWORD_POLICY", + "ROLE", + "RULE", + "SOD_POLICY", + "SOURCE", + "TAG", + "TAG_CATEGORY", + "TASK_RESULT", + "REPORT_RESULT", + "SOD_VIOLATION", + "ACCOUNT_ACTIVITY", + "WORKGROUP" + ], + "description": "An enumeration of the types of DTOs supported within the IdentityNow infrastructure.", + "example": "IDENTITY" + }, + "description": { + "type": "string", + "nullable": true, + "example": null + } + } + } + ] + }, + { + "type": "object", + "properties": { + "source": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + "privileged": { + "type": "boolean", + "example": false + }, + "attribute": { + "type": "string", + "example": "memberOf" + }, + "value": { + "type": "string", + "example": "CN=Buyer,OU=Groups,OU=Demo,DC=seri,DC=sailpointdemo,DC=com" + }, + "standalone": { + "type": "boolean", + "example": false + } + } + } + ] + }, + { + "description": "Role", + "allOf": [ + { + "allOf": [ + { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "example": "John Q. Doe" + } + } + } + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "ACCOUNT_CORRELATION_CONFIG", + "ACCESS_PROFILE", + "ACCESS_REQUEST_APPROVAL", + "ACCOUNT", + "APPLICATION", + "CAMPAIGN", + "CAMPAIGN_FILTER", + "CERTIFICATION", + "CLUSTER", + "CONNECTOR_SCHEMA", + "ENTITLEMENT", + "GOVERNANCE_GROUP", + "IDENTITY", + "IDENTITY_PROFILE", + "IDENTITY_REQUEST", + "LIFECYCLE_STATE", + "PASSWORD_POLICY", + "ROLE", + "RULE", + "SOD_POLICY", + "SOURCE", + "TAG", + "TAG_CATEGORY", + "TASK_RESULT", + "REPORT_RESULT", + "SOD_VIOLATION", + "ACCOUNT_ACTIVITY", + "WORKGROUP" + ], + "description": "An enumeration of the types of DTOs supported within the IdentityNow infrastructure.", + "example": "IDENTITY" + }, + "description": { + "type": "string", + "nullable": true, + "example": null + } + } + } + ] + }, + { + "type": "object", + "properties": { + "owner": { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + }, + { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "example": "John Q. Doe" + } + } + } + ] + }, + "disabled": { + "type": "boolean" + }, + "revocable": { + "type": "boolean" + } + } + } + ] + } + ] + } + }, + "accessCount": { + "type": "integer", + "format": "int32", + "description": "Number of access items assigned to the identity.", + "example": 5 + }, + "entitlementCount": { + "type": "integer", + "format": "int32", + "description": "Number of entitlements assigned to the identity.", + "example": 10 + }, + "roleCount": { + "type": "integer", + "format": "int32", + "description": "Number of roles assigned to the identity.", + "example": 1 + }, + "accessProfileCount": { + "type": "integer", + "format": "int32", + "description": "Number of access profiles assigned to the identity.", + "example": 1 + }, + "owns": { + "type": "object", + "description": "Access items the identity owns.", + "properties": { + "sources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + } + }, + "entitlements": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + } + }, + "accessProfiles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + } + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + } + }, + "apps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + } + }, + "governanceGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "2c91808568c529c60168cca6f90c1313", + "description": "The unique ID of the referenced object." + }, + "name": { + "type": "string", + "example": "John Doe", + "description": "The human readable name of the referenced object." + } + } + } + }, + "fallbackApprover": { + "type": "boolean", + "example": false + } + }, + "ownsCount": { + "type": "integer", + "format": "int32", + "description": "Number of access items the identity owns.", + "example": 5 + }, + "tags": { + "type": "array", + "description": "Tags that have been applied to the object.", + "items": { + "type": "string" + }, + "example": [ + "TAG_1", + "TAG_2" + ] + } + } + } \ No newline at end of file diff --git a/src/commands/constants.ts b/src/commands/constants.ts index 460ae21..208802c 100644 --- a/src/commands/constants.ts +++ b/src/commands/constants.ts @@ -1,101 +1,110 @@ -export const OPEN_RESOURCE = 'vscode-sailpoint-identitynow.open-resource'; -export const REMOVE_RESOURCE = 'vscode-sailpoint-identitynow.remove-resource'; -export const REFRESH_FORCED = 'vscode-sailpoint-identitynow.refresh-forced'; -export const REFRESH = 'vscode-sailpoint-identitynow.refresh'; -export const ADD_TENANT = 'vscode-sailpoint-identitynow.add-tenant'; -export const RENAME_TENANT = 'vscode-sailpoint-identitynow.rename-tenant'; -export const REMOVE_TENANT = 'vscode-sailpoint-identitynow.remove-tenant'; -export const EXPORT_CONFIG_VIEW = 'vscode-sailpoint-identitynow.export-config.view'; -export const EXPORT_CONFIG_PALETTE = 'vscode-sailpoint-identitynow.export-config.palette'; -export const EXPORT_NODE_CONFIG_VIEW = 'vscode-sailpoint-identitynow.export-node-config.view'; -export const IMPORT_CONFIG_VIEW = 'vscode-sailpoint-identitynow.import-config.view'; -export const IMPORT_CONFIG_PALETTE = 'vscode-sailpoint-identitynow.import-config.palette'; -export const IMPORT_CONFIG_MENU = 'vscode-sailpoint-identitynow.import-config.menu'; -export const AGGREGATE = 'vscode-sailpoint-identitynow.aggregate-source'; -export const AGGREGATE_DISABLE_OPTIMIZATION = 'vscode-sailpoint-identitynow.aggregate-source-disable-optimization'; -export const AGGREGATE_ENTITLEMENTS = 'vscode-sailpoint-identitynow.aggregate-entitlements'; -export const UPLOAD_FILE = 'vscode-sailpoint-identitynow.source.upload-file'; -export const IMPORT_ACCOUNTS_VIEW = 'vscode-sailpoint-identitynow.source.import.accounts.view'; -export const EXPORT_ACCOUNTS_VIEW = 'vscode-sailpoint-identitynow.source.export.accounts.view'; -export const IMPORT_UNCORRELATED_ACCOUNTS_VIEW = 'vscode-sailpoint-identitynow.source.import.uncorrelated-accounts.view'; -export const EXPORT_UNCORRELATED_ACCOUNTS_VIEW = 'vscode-sailpoint-identitynow.source.export.uncorrelated-accounts.view'; -export const IMPORT_ENTITLEMENT_DETAILS_VIEW = 'vscode-sailpoint-identitynow.source.import.entitlement-details.view'; -export const EXPORT_ENTITLEMENT_DETAILS_VIEW = 'vscode-sailpoint-identitynow.source.export.entitlement-details.view'; -export const TEST_SOURCE = 'vscode-sailpoint-identitynow.test-source'; -export const TEST_SOURCE_PALETTE = 'vscode-sailpoint-identitynow.test-source.palette'; -export const PEEK_SOURCE = 'vscode-sailpoint-identitynow.peek-source'; -export const PEEK_SOURCE_PALETTE = 'vscode-sailpoint-identitynow.peek-source.palette'; -export const PING_SOURCE = 'vscode-sailpoint-identitynow.ping-source'; -export const PING_SOURCE_PALETTE = 'vscode-sailpoint-identitynow.ping-source.palette'; -export const CLONE_SOURCE = 'vscode-sailpoint-identitynow.clone-source'; -export const CLONE_SOURCE_PALETTE = 'vscode-sailpoint-identitynow.clone-source.palette'; -export const RESET_SOURCE = 'vscode-sailpoint-identitynow.reset-source'; -export const RESET_SOURCE_ACCOUNTS = 'vscode-sailpoint-identitynow.reset-source-accounts'; -export const RESET_SOURCE_ENTITLEMENTS = 'vscode-sailpoint-identitynow.reset-source-entitlements'; -export const NEW_TRANSFORM = 'vscode-sailpoint-identitynow.new-transform'; -export const NEW_PROVISIONING_POLICY = 'vscode-sailpoint-identitynow.new-provisioning-policy'; -export const NEW_SCHEMA = 'vscode-sailpoint-identitynow.new-schema'; -export const EVALUATE_TRANSFORM = 'vscode-sailpoint-identitynow.transform.evaluate'; -export const EVALUATE_TRANSFORM_EDITOR = 'vscode-sailpoint-identitynow.transform.evaluate.editor'; -export const ENABLE_WORKFLOW = 'vscode-sailpoint-identitynow.workflow.enable'; -export const DISABLE_WORKFLOW = 'vscode-sailpoint-identitynow.workflow.disable'; -export const EXPORT_WORKFLOW = 'vscode-sailpoint-identitynow.workflow.export'; -export const IMPORT_WORKFLOW = 'vscode-sailpoint-identitynow.workflow.import'; -export const IMPORT_WORKFLOW_VIEW_ICON = 'vscode-sailpoint-identitynow.workflow.import.view-icon'; -export const TEST_WORKFLOW = 'vscode-sailpoint-identitynow.workflow.test'; -export const VIEW_WORKFLOW_EXECUTION_HISTORY = 'vscode-sailpoint-identitynow.workflow.view-execution-history'; -export const EDIT_CONNECTOR_RULE = 'vscode-sailpoint-identitynow.connector-rule.edit'; -export const NEW_CONNECTOR_RULE = 'vscode-sailpoint-identitynow.new-connector-rule'; -export const UPLOAD_CONNECTOR_RULE = 'vscode-sailpoint-identitynow.connector-rule.upload'; -export const VALIDATE_CONNECTOR_RULE = 'vscode-sailpoint-identitynow.connector-rule.validate'; -export const EXPORT_CONNECTOR_RULE_SCRIPT_EDITOR = 'vscode-sailpoint-identitynow.connector-rule.export-script.editor'; -export const EXPORT_CONNECTOR_RULE_SCRIPT_VIEW = 'vscode-sailpoint-identitynow.connector-rule.export-script.view'; -export const SORT_IDENTITY_PROFILES_BY_NAME = 'vscode-sailpoint-identitynow.identity-profiles.sort.name'; -export const SORT_IDENTITY_PROFILES_BY_PRIORITY = 'vscode-sailpoint-identitynow.identity-profiles.sort.priority'; -export const REFRESH_IDENTITY_PROFILE = 'vscode-sailpoint-identitynow.identity-profile.refresh'; -export const TREE_VIEW = 'vscode-sailpoint-identitynow.view'; -export const WORKFLOW_TESTER_VIEW = 'vscode-sailpoint-identitynow.workflow.test-view'; +const COMMAND_PREFIX = 'vscode-sailpoint-identitynow'; +export const OPEN_RESOURCE = `${COMMAND_PREFIX}.open-resource`; +export const REMOVE_RESOURCE = `${COMMAND_PREFIX}.remove-resource`; +export const REFRESH_FORCED = `${COMMAND_PREFIX}.refresh-forced`; +export const REFRESH = `${COMMAND_PREFIX}.refresh`; +export const ADD_TENANT = `${COMMAND_PREFIX}.add-tenant`; +export const RENAME_TENANT = `${COMMAND_PREFIX}.rename-tenant`; +export const REMOVE_TENANT = `${COMMAND_PREFIX}.remove-tenant`; +export const EXPORT_CONFIG_VIEW = `${COMMAND_PREFIX}.export-config.view`; +export const EXPORT_CONFIG_PALETTE = `${COMMAND_PREFIX}.export-config.palette`; +export const EXPORT_NODE_CONFIG_VIEW = `${COMMAND_PREFIX}.export-node-config.view`; +export const IMPORT_CONFIG_VIEW = `${COMMAND_PREFIX}.import-config.view`; +export const IMPORT_CONFIG_PALETTE = `${COMMAND_PREFIX}.import-config.palette`; +export const IMPORT_CONFIG_MENU = `${COMMAND_PREFIX}.import-config.menu`; +export const AGGREGATE = `${COMMAND_PREFIX}.aggregate-source`; +export const AGGREGATE_DISABLE_OPTIMIZATION = `${COMMAND_PREFIX}.aggregate-source-disable-optimization`; +export const AGGREGATE_ENTITLEMENTS = `${COMMAND_PREFIX}.aggregate-entitlements`; +export const UPLOAD_FILE = `${COMMAND_PREFIX}.source.upload-file`; +export const IMPORT_ACCOUNTS_VIEW = `${COMMAND_PREFIX}.source.import.accounts.view`; +export const EXPORT_ACCOUNTS_VIEW = `${COMMAND_PREFIX}.source.export.accounts.view`; +export const IMPORT_UNCORRELATED_ACCOUNTS_VIEW = `${COMMAND_PREFIX}.source.import.uncorrelated-accounts.view`; +export const EXPORT_UNCORRELATED_ACCOUNTS_VIEW = `${COMMAND_PREFIX}.source.export.uncorrelated-accounts.view`; +export const IMPORT_ENTITLEMENT_DETAILS_VIEW = `${COMMAND_PREFIX}.source.import.entitlement-details.view`; +export const EXPORT_ENTITLEMENT_DETAILS_VIEW = `${COMMAND_PREFIX}.source.export.entitlement-details.view`; +export const TEST_SOURCE = `${COMMAND_PREFIX}.test-source`; +export const TEST_SOURCE_PALETTE = `${COMMAND_PREFIX}.test-source.palette`; +export const PEEK_SOURCE = `${COMMAND_PREFIX}.peek-source`; +export const PEEK_SOURCE_PALETTE = `${COMMAND_PREFIX}.peek-source.palette`; +export const PING_SOURCE = `${COMMAND_PREFIX}.ping-source`; +export const PING_SOURCE_PALETTE = `${COMMAND_PREFIX}.ping-source.palette`; +export const CLONE_SOURCE = `${COMMAND_PREFIX}.clone-source`; +export const CLONE_SOURCE_PALETTE = `${COMMAND_PREFIX}.clone-source.palette`; +export const RESET_SOURCE = `${COMMAND_PREFIX}.reset-source`; +export const RESET_SOURCE_ACCOUNTS = `${COMMAND_PREFIX}.reset-source-accounts`; +export const RESET_SOURCE_ENTITLEMENTS = `${COMMAND_PREFIX}.reset-source-entitlements`; +export const NEW_TRANSFORM = `${COMMAND_PREFIX}.new-transform`; +export const NEW_PROVISIONING_POLICY = `${COMMAND_PREFIX}.new-provisioning-policy`; +export const NEW_SCHEMA = `${COMMAND_PREFIX}.new-schema`; +export const EVALUATE_TRANSFORM = `${COMMAND_PREFIX}.transform.evaluate`; +export const EVALUATE_TRANSFORM_EDITOR = `${COMMAND_PREFIX}.transform.evaluate.editor`; +export const ENABLE_WORKFLOW = `${COMMAND_PREFIX}.workflow.enable`; +export const DISABLE_WORKFLOW = `${COMMAND_PREFIX}.workflow.disable`; +export const EXPORT_WORKFLOW = `${COMMAND_PREFIX}.workflow.export`; +export const IMPORT_WORKFLOW = `${COMMAND_PREFIX}.workflow.import`; +export const IMPORT_WORKFLOW_VIEW_ICON = `${COMMAND_PREFIX}.workflow.import.view-icon`; +export const TEST_WORKFLOW = `${COMMAND_PREFIX}.workflow.test`; +export const VIEW_WORKFLOW_EXECUTION_HISTORY = `${COMMAND_PREFIX}.workflow.view-execution-history`; +export const EDIT_CONNECTOR_RULE = `${COMMAND_PREFIX}.connector-rule.edit`; +export const NEW_CONNECTOR_RULE = `${COMMAND_PREFIX}.new-connector-rule`; +export const UPLOAD_CONNECTOR_RULE = `${COMMAND_PREFIX}.connector-rule.upload`; +export const VALIDATE_CONNECTOR_RULE = `${COMMAND_PREFIX}.connector-rule.validate`; +export const EXPORT_CONNECTOR_RULE_SCRIPT_EDITOR = `${COMMAND_PREFIX}.connector-rule.export-script.editor`; +export const EXPORT_CONNECTOR_RULE_SCRIPT_VIEW = `${COMMAND_PREFIX}.connector-rule.export-script.view`; +export const SORT_IDENTITY_PROFILES_BY_NAME = `${COMMAND_PREFIX}.identity-profiles.sort.name`; +export const SORT_IDENTITY_PROFILES_BY_PRIORITY = `${COMMAND_PREFIX}.identity-profiles.sort.priority`; +export const REFRESH_IDENTITY_PROFILE = `${COMMAND_PREFIX}.identity-profile.refresh`; +export const TREE_VIEW = `${COMMAND_PREFIX}.view`; +export const WORKFLOW_TESTER_VIEW = `${COMMAND_PREFIX}.workflow.test-view`; -export const LOAD_MORE = 'vscode-sailpoint-identitynow.load-more'; +export const LOAD_MORE = `${COMMAND_PREFIX}.load-more`; // New Access Profile -export const NEW_ACCESS_PROFILE_VIEW = 'vscode-sailpoint-identitynow.new-access-profile.view'; -export const NEW_ACCESS_PROFILE_VIEW_ICON = 'vscode-sailpoint-identitynow.new-access-profile.view-icon'; -export const NEW_ACCESS_PROFILE_PALETTE = 'vscode-sailpoint-identitynow.new-access-profile.palette'; +export const NEW_ACCESS_PROFILE_VIEW = `${COMMAND_PREFIX}.new-access-profile.view`; +export const NEW_ACCESS_PROFILE_VIEW_ICON = `${COMMAND_PREFIX}.new-access-profile.view-icon`; +export const NEW_ACCESS_PROFILE_PALETTE = `${COMMAND_PREFIX}.new-access-profile.palette`; // New Role -export const NEW_ROLE_VIEW = 'vscode-sailpoint-identitynow.new-role.view'; -export const NEW_ROLE_VIEW_ICON = 'vscode-sailpoint-identitynow.new-role.view-icon'; -export const NEW_ROLE_PALETTE = 'vscode-sailpoint-identitynow.new-role.palette'; +export const NEW_ROLE_VIEW = `${COMMAND_PREFIX}.new-role.view`; +export const NEW_ROLE_VIEW_ICON = `${COMMAND_PREFIX}.new-role.view-icon`; +export const NEW_ROLE_PALETTE = `${COMMAND_PREFIX}.new-role.palette`; // Access Profile Import / Export -export const EXPORT_ACCESS_PROFILE_VIEW = 'vscode-sailpoint-identitynow.csv.export.access-profiles.view'; -export const EXPORT_ACCESS_PROFILE_ICON_VIEW = 'vscode-sailpoint-identitynow.csv.export.access-profiles-icon.view'; -export const IMPORT_ACCESS_PROFILE_VIEW = 'vscode-sailpoint-identitynow.csv.import.access-profiles.view'; -export const IMPORT_ACCESS_PROFILE_ICON_VIEW = 'vscode-sailpoint-identitynow.csv.import.access-profiles-icon.view'; -export const IMPORT_ACCESS_PROFILE_EXPLORER = 'vscode-sailpoint-identitynow.csv.import.access-profiles.explorer'; -export const ACCESS_PROFILE_FILTER_VIEW = 'vscode-sailpoint-identitynow.access-profiles.filter'; -export const ACCESS_PROFILE_UPDATE_FILTER_VIEW = 'vscode-sailpoint-identitynow.access-profiles.update-filter'; +export const EXPORT_ACCESS_PROFILE_VIEW = `${COMMAND_PREFIX}.csv.export.access-profiles.view`; +export const EXPORT_ACCESS_PROFILE_ICON_VIEW = `${COMMAND_PREFIX}.csv.export.access-profiles-icon.view`; +export const IMPORT_ACCESS_PROFILE_VIEW = `${COMMAND_PREFIX}.csv.import.access-profiles.view`; +export const IMPORT_ACCESS_PROFILE_ICON_VIEW = `${COMMAND_PREFIX}.csv.import.access-profiles-icon.view`; +export const IMPORT_ACCESS_PROFILE_EXPLORER = `${COMMAND_PREFIX}.csv.import.access-profiles.explorer`; +export const ACCESS_PROFILE_FILTER_VIEW = `${COMMAND_PREFIX}.access-profiles.filter`; +export const ACCESS_PROFILE_UPDATE_FILTER_VIEW = `${COMMAND_PREFIX}.access-profiles.update-filter`; // Role Import / Export -export const EXPORT_ROLE_VIEW = 'vscode-sailpoint-identitynow.csv.export.roles.view'; -export const EXPORT_ROLE_ICON_VIEW = 'vscode-sailpoint-identitynow.csv.export.roles-icon.view'; -export const IMPORT_ROLE_VIEW = 'vscode-sailpoint-identitynow.csv.import.roles.view'; -export const IMPORT_ROLE_ICON_VIEW = 'vscode-sailpoint-identitynow.csv.import.roles-icon.view'; -export const IMPORT_ROLE_EXPLORER = 'vscode-sailpoint-identitynow.csv.import.roles.explorer'; -export const ROLE_FILTER_VIEW = 'vscode-sailpoint-identitynow.roles.filter'; -export const ROLE_UPDATE_FILTER_VIEW = 'vscode-sailpoint-identitynow.roles.update-filter'; +export const EXPORT_ROLE_VIEW = `${COMMAND_PREFIX}.csv.export.roles.view`; +export const EXPORT_ROLE_ICON_VIEW = `${COMMAND_PREFIX}.csv.export.roles-icon.view`; +export const IMPORT_ROLE_VIEW = `${COMMAND_PREFIX}.csv.import.roles.view`; +export const IMPORT_ROLE_ICON_VIEW = `${COMMAND_PREFIX}.csv.import.roles-icon.view`; +export const IMPORT_ROLE_EXPLORER = `${COMMAND_PREFIX}.csv.import.roles.explorer`; +export const ROLE_FILTER_VIEW = `${COMMAND_PREFIX}.roles.filter`; +export const ROLE_UPDATE_FILTER_VIEW = `${COMMAND_PREFIX}.roles.update-filter`; // FORMS -export const EXPORT_FORMS_VIEW = 'vscode-sailpoint-identitynow.forms.export.view'; -export const EXPORT_FORMS_ICON_VIEW = 'vscode-sailpoint-identitynow.forms.export.icon-view'; -export const EXPORT_FORM_VIEW = 'vscode-sailpoint-identitynow.form.export.view'; -export const IMPORT_FORMS_VIEW = 'vscode-sailpoint-identitynow.forms.import.view'; -export const IMPORT_FORMS_ICON_VIEW = 'vscode-sailpoint-identitynow.forms.import.icon-view'; +export const EXPORT_FORMS_VIEW = `${COMMAND_PREFIX}.forms.export.view`; +export const EXPORT_FORMS_ICON_VIEW = `${COMMAND_PREFIX}.forms.export.icon-view`; +export const EXPORT_FORM_VIEW = `${COMMAND_PREFIX}.form.export.view`; +export const IMPORT_FORMS_VIEW = `${COMMAND_PREFIX}.forms.import.view`; +export const IMPORT_FORMS_ICON_VIEW = `${COMMAND_PREFIX}.forms.import.icon-view`; -export const EDIT_PUBLIC_IDENTITIES_CONFIG = 'vscode-sailpoint-identitynow.tenant.edit.public-identities-config'; -export const EDIT_ACCESS_REQUEST_CONFIG = 'vscode-sailpoint-identitynow.tenant.edit.access-request-config'; -export const EDIT_PASSWORD_ORG_CONFIG = 'vscode-sailpoint-identitynow.tenant.edit.password-org-config'; -export const GENERATE_DIGIT_TOKEN = 'vscode-sailpoint-identitynow.tenant.generate-digit-token'; +export const EDIT_PUBLIC_IDENTITIES_CONFIG = `${COMMAND_PREFIX}.tenant.edit.public-identities-config`; +export const EDIT_ACCESS_REQUEST_CONFIG = `${COMMAND_PREFIX}.tenant.edit.access-request-config`; +export const EDIT_PASSWORD_ORG_CONFIG = `${COMMAND_PREFIX}.tenant.edit.password-org-config`; +export const GENERATE_DIGIT_TOKEN = `${COMMAND_PREFIX}.tenant.generate-digit-token`; -export const NEW_SEARCH_ATTRIBUTE = 'vscode-sailpoint-identitynow.new-attribute-search.view' \ No newline at end of file +//Search Attribute +export const NEW_SEARCH_ATTRIBUTE = `${COMMAND_PREFIX}.new-attribute-search.view`; + +//Identities +export const IDENTITIES_SEARCH = `${COMMAND_PREFIX}.identities.search`; +export const IDENTITIES_ICON_SEARCH = `${COMMAND_PREFIX}.identities.icon-search`; +export const IDENTITIES_DELETE = `${COMMAND_PREFIX}.identities.delete`; +export const IDENTITIES_ATT_SYNC = `${COMMAND_PREFIX}.identities.att-sync`; +export const IDENTITIES_PROCESS = `${COMMAND_PREFIX}.identities.process`; \ No newline at end of file diff --git a/src/commands/filterCommand.ts b/src/commands/filterCommand.ts index 57a6893..8ed3df8 100644 --- a/src/commands/filterCommand.ts +++ b/src/commands/filterCommand.ts @@ -104,3 +104,25 @@ export class RoleFilterCommand extends FilterCommand { } } +export class IdentityDefinitionFilterCommand extends FilterCommand { + constructor() { + super(); + } + + protected async runWizard(filterType: FilterType, wizardContext: WizardContext): Promise { + return await runWizard({ + title: "Search identities", + hideStepCount: true, + promptSteps: [ + newFilter(filterType), + new FilterInputStep("identities", + FilterType.search, + "https://documentation.sailpoint.com/saas/help/search/searchable-fields.html#searching-identity-data"), + new FilterInputStep("identities", + FilterType.api, + "https://developer.sailpoint.com/docs/api/beta/list-identities") + ], + }, wizardContext); + } +} + diff --git a/src/commands/identity/IdentityTreeViewCommand.ts b/src/commands/identity/IdentityTreeViewCommand.ts new file mode 100644 index 0000000..07a79c1 --- /dev/null +++ b/src/commands/identity/IdentityTreeViewCommand.ts @@ -0,0 +1,53 @@ +import * as vscode from 'vscode'; + +import { IdentitiesTreeItem } from '../../models/ISCTreeItem'; +import { ISCClient } from '../../services/ISCClient'; + +export class IdentityTreeViewCommand { + + constructor() { } + + async deleteIdentity(identityTreeItem?: IdentitiesTreeItem): Promise { + console.log("> IdentityDefinitionTreeViewCommand.deleteIdentity"); + + if (identityTreeItem === undefined) { + return; + } + + vscode.window + .showInformationMessage(`Are you sure you want to delete ${identityTreeItem.label}?`, "Yes", "No") + .then(async (answer) => { + if (answer === "Yes") { + const client = new ISCClient(identityTreeItem.tenantId, identityTreeItem.tenantName); + await client.deleteIdentity(identityTreeItem.id); + vscode.window.showInformationMessage("Identity Delete started"); + } + }) + } + + async attSyncIdentity(identityTreeItem?: IdentitiesTreeItem): Promise { + console.log("> IdentityDefinitionTreeViewCommand.attSyncIdentity"); + + if (identityTreeItem === undefined) { + return; + } + + const client = new ISCClient(identityTreeItem.tenantId, identityTreeItem.tenantName); + await client.syncIdentityAttributes(identityTreeItem.id); + + vscode.window.showInformationMessage("Identity Att Sync started"); + } + + async processIdentity(identityTreeItem?: IdentitiesTreeItem): Promise { + console.log("> IdentityDefinitionTreeViewCommand.processIdentity"); + + if (identityTreeItem === undefined) { + return; + } + + const client = new ISCClient(identityTreeItem.tenantId, identityTreeItem.tenantName); + await client.processIdentity(identityTreeItem.id); + + vscode.window.showInformationMessage("Identity process started"); + } +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 569ecb4..8a0b5c8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -10,7 +10,7 @@ import { deleteResource } from './commands/deleteResource'; import { AccountExporterCommand, UncorrelatedAccountExporterCommand } from './commands/source/exportAccounts'; import { EntitlementExporterCommand as EntitlementDetailsExporterCommand } from './commands/source/exportEntitlementDetails'; import { ExportScriptFromRuleCommand } from './commands/rule/exportScriptFromRuleCommand'; -import { AccessProfileFilterCommand, RoleFilterCommand } from './commands/filterCommand'; +import { AccessProfileFilterCommand, RoleFilterCommand, IdentityDefinitionFilterCommand } from './commands/filterCommand'; import { AccountImportNodeCommand } from './commands/source/importAccount'; import { EntitlementDetailsImportNodeCommand } from './commands/source/importEntitlementDetails'; import { UncorrelatedAccountImportNodeCommand } from './commands/source/importUncorrelatedAccount'; @@ -61,6 +61,7 @@ import { GenerateDigitTokenCommand } from './commands/tenant/generateDigitTokenC import { onErrorResponse, onRequest, onResponse } from './services/AxiosHandlers'; import axios from 'axios'; import { OpenScriptCommand } from './commands/rule/openScriptCommand'; +import { IdentityTreeViewCommand } from './commands/identity/IdentityTreeViewCommand'; // this method is called when your extension is activated // your extension is activated the very first time the command is executed @@ -452,6 +453,25 @@ export function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand(commands.NEW_SEARCH_ATTRIBUTE, newAttributeSearchConfigCommand.execute, newAttributeSearchConfigCommand)); + // Identity Definition Config + const newIdentityCommand = new IdentityTreeViewCommand() + const identityFilterCommand = new IdentityDefinitionFilterCommand(); + context.subscriptions.push( + vscode.commands.registerCommand(commands.IDENTITIES_SEARCH, + identityFilterCommand.execute, identityFilterCommand)); + context.subscriptions.push( + vscode.commands.registerCommand(commands.IDENTITIES_ICON_SEARCH, + identityFilterCommand.execute, identityFilterCommand)); + context.subscriptions.push( + vscode.commands.registerCommand(commands.IDENTITIES_ATT_SYNC, + newIdentityCommand.attSyncIdentity, newIdentityCommand)); + context.subscriptions.push( + vscode.commands.registerCommand(commands.IDENTITIES_DELETE, + newIdentityCommand.deleteIdentity, newIdentityCommand)); + context.subscriptions.push( + vscode.commands.registerCommand(commands.IDENTITIES_PROCESS, + newIdentityCommand.processIdentity, newIdentityCommand)); + axios.interceptors.request.use(onRequest) diff --git a/src/files/ISCResourceProvider.ts b/src/files/ISCResourceProvider.ts index 167dd15..19e8ff6 100644 --- a/src/files/ISCResourceProvider.ts +++ b/src/files/ISCResourceProvider.ts @@ -42,6 +42,7 @@ export class ISCResourceProvider implements FileSystemProvider { // Not optimized here but do not const data = await this.lookupResource(uri); const id = getIdByUri(uri); + const resourcePath = getPathByUri(uri); // const isFile = this.isUUID(id); const isFile = id !== "provisioning-policies" && id !== "schemas"; return { @@ -49,6 +50,7 @@ export class ISCResourceProvider implements FileSystemProvider { ctime: toTimestamp(data.created), mtime: toTimestamp(data.modified), size: convertToText(data).length, + permissions: resourcePath.match("identities") ? vscode.FilePermission.Readonly : null }; } readDirectory( @@ -91,9 +93,22 @@ export class ISCResourceProvider implements FileSystemProvider { if (/\/connector-rule-script\//.test(resourcePath)) { const rule = await client.getConnectorRuleById(id); data = rule.sourceCode?.script + } else if (/\/identities\//.test(resourcePath)) { + const response = await client.paginatedSearchIdentities( + `id:${id}`, + 2, + 0, + false, + null, + true + ); + if (response.data.length ===1 ) { + data = response.data[0] + } } else { data = await client.getResource(resourcePath); } + if (!data) { throw vscode.FileSystemError.FileNotFound(uri); } @@ -230,7 +245,11 @@ export class ISCResourceProvider implements FileSystemProvider { patchResourcePath, JSON.stringify(jsonpatch) ); - } else { + } else if (resourcePath.match("identities")) { + console.log("save identities - cant do this folks"); + vscode.window.showErrorMessage("Identities cannot be modified directly"); + } + else { // Need to update the content to remove id and internal properties from the payload // to prevent a bad request error if (resourcePath.match("transform")) { diff --git a/src/models/ISCTreeItem.ts b/src/models/ISCTreeItem.ts index f30e57a..9a2be47 100644 --- a/src/models/ISCTreeItem.ts +++ b/src/models/ISCTreeItem.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { ISCClient, TOTAL_COUNT_HEADER } from "../services/ISCClient"; import { getIdByUri, getPathByUri, getResourceUri } from "../utils/UriUtils"; import { compareByLabel, compareByName, compareByPriority } from "../utils"; -import { AxiosResponse } from "axios"; +import { AxiosHeaders, AxiosResponse } from "axios"; import { getConfigNumber } from '../utils/configurationUtils'; import * as commands from "../commands/constants"; import * as configuration from '../configurationConstants'; @@ -73,6 +73,7 @@ export class TenantTreeItem extends BaseTreeItem { results.push(new FormsTreeItem(this.tenantId, this.tenantName, this.tenantDisplayName)); results.push(new SearchAttributesTreeItem(this.tenantId, this.tenantName, this.tenantDisplayName)); results.push(new IdentityAttributesTreeItem(this.tenantId, this.tenantName, this.tenantDisplayName)); + results.push(new IdentitiesTreeItem(this.tenantId, this.tenantName, this.tenantDisplayName)); return new Promise((resolve) => resolve(results)); } @@ -762,13 +763,23 @@ export abstract class PageableFolderTreeItem extends FolderTreeItem implement protected _total = 0; + /** + * + * @param label + * @param contextValue + * @param tenantId + * @param tenantName + * @param tenantDisplayName + * @param noEntries Message to be displayed if the search does not return anything, or if a search needs to be run (cf. identities) + * @param mapper + */ constructor( label: string, contextValue: string, tenantId: string, tenantName: string, tenantDisplayName: string, - private readonly notFoundMessage: string, + protected noEntries: string, private readonly mapper: (x: any) => BaseTreeItem, ) { super(label, contextValue, tenantId, tenantName, tenantDisplayName); @@ -802,7 +813,7 @@ export abstract class PageableFolderTreeItem extends FolderTreeItem implement } if (this._total === 0) { - this.children = [new MessageNode(this.notFoundMessage)]; + this.children = [new MessageNode(this.noEntries)]; return; } @@ -1168,4 +1179,80 @@ export class IdentityAttributeTreeItem extends ISCResourceTreeItem { } iconPath = new vscode.ThemeIcon("list-selection"); +} + +/* Contain Identity Definition */ +export class IdentitiesTreeItem extends PageableFolderTreeItem { + constructor( + tenantId: string, + tenantName: string, + tenantDisplayName: string, + ) { + super("Identities", "identities", tenantId, tenantName, tenantDisplayName, 'No identities found', + (identity => new IdentityTreeItem( + tenantId, + tenantName, + tenantDisplayName, + identity.name, + identity.id + )) + ); + } + protected async loadNext(): Promise> { + const limit = getConfigNumber(configuration.TREEVIEW_PAGINATION).valueOf(); + if (!isEmpty(this.filters)) { + this.noEntries = "No identities found"; + if (this.filterType === FilterType.api) { + return await this.client.listIdentities({ + filters: this.filters, + limit, + offset: this.currentOffset, + count: (this._total === 0) + }) as AxiosResponse; + } else { + const filters = isEmpty(this.filters) ? "*" : this.filters; + return await this.client.paginatedSearchIdentities( + filters, + limit, + this.currentOffset, + (this._total === 0) + ) as AxiosResponse; + } + } + else { + //Force return nothing + this.noEntries = "Use search to load identities"; + return { + headers: new AxiosHeaders({ + [TOTAL_COUNT_HEADER]: 0 + }), + data: null, + status: 200, + statusText: "", + config: null + } + } + } +} + +export class IdentityTreeItem extends ISCResourceTreeItem { + contextValue = "identity"; + + constructor(tenantId: string, + tenantName: string, + tenantDisplayName: string, + label: string, + id: string) { + super({ + tenantId, + tenantName, + tenantDisplayName, + label, + resourceType: "identities", + id, + beta: true + }) + } + + iconPath = new vscode.ThemeIcon("person"); } \ No newline at end of file diff --git a/src/services/ISCClient.ts b/src/services/ISCClient.ts index a950324..8a4a5f7 100644 --- a/src/services/ISCClient.ts +++ b/src/services/ISCClient.ts @@ -5,7 +5,7 @@ import { withQuery } from "../utils/UriUtils"; import { compareByName, convertToText } from "../utils"; import { DEFAULT_ACCOUNTS_QUERY_PARAMS } from "../models/Account"; import { DEFAULT_ENTITLEMENTS_QUERY_PARAMS } from "../models/Entitlements"; -import { Configuration, IdentityProfilesApi, IdentityProfile, LifecycleState, LifecycleStatesApi, Paginator, ServiceDeskIntegrationApi, ServiceDeskIntegrationDto, Source, SourcesApi, Transform, TransformsApi, WorkflowsBetaApi, WorkflowBeta, WorkflowExecutionBeta, WorkflowLibraryTriggerBeta, ConnectorRuleManagementBetaApi, ConnectorRuleResponseBeta, ConnectorRuleValidationResponseBeta, AccountsApi, AccountsApiListAccountsRequest, Account, EntitlementsBetaApi, EntitlementsBetaApiListEntitlementsRequest, PublicIdentitiesApi, PublicIdentitiesApiGetPublicIdentitiesRequest, Entitlement, PublicIdentity, JsonPatchOperationBeta, SPConfigBetaApi, SpConfigImportResultsBeta, SpConfigJobBeta, ImportOptionsBeta, SpConfigExportResultsBeta, ObjectExportImportOptionsBeta, ExportPayloadBetaIncludeTypesEnum, ImportSpConfigRequestBeta, TransformRead, GovernanceGroupsBetaApi, WorkgroupDtoBeta, AccessProfilesApi, AccessProfilesApiListAccessProfilesRequest, AccessProfile, RolesApi, Role, RolesApiListRolesRequest, Search, SearchApi, IdentityDocument, SearchDocument, AccessProfileDocument, EntitlementDocument, EntitlementBeta, RoleDocument, SourcesBetaApi, StatusResponseBeta, Schema, ConnectorsBetaApi, FormBeta, CustomFormsBetaApi, ExportFormDefinitionsByTenant200ResponseInnerBeta, FormDefinitionResponseBeta, NotificationsBetaApi, TemplateDtoBeta, SegmentsApi, Segment, SODPolicyApi, SodPolicy, SearchAttributeConfigurationBetaApi, SearchAttributeConfigBeta, IdentityAttributesBetaApi, IdentityAttributeBeta, PasswordConfigurationApi, PasswordOrgConfig, PasswordManagementApi, PasswordManagementBetaApi, ConnectorRuleUpdateRequestBeta } from 'sailpoint-api-client'; +import { Configuration, IdentityProfilesApi, IdentityProfile, LifecycleState, LifecycleStatesApi, Paginator, ServiceDeskIntegrationApi, ServiceDeskIntegrationDto, Source, SourcesApi, TransformsApi, WorkflowsBetaApi, WorkflowBeta, WorkflowExecutionBeta, WorkflowLibraryTriggerBeta, ConnectorRuleManagementBetaApi, ConnectorRuleResponseBeta, ConnectorRuleValidationResponseBeta, AccountsApi, AccountsApiListAccountsRequest, Account, EntitlementsBetaApi, EntitlementsBetaApiListEntitlementsRequest, PublicIdentitiesApi, PublicIdentitiesApiGetPublicIdentitiesRequest, PublicIdentity, JsonPatchOperationBeta, SPConfigBetaApi, SpConfigImportResultsBeta, SpConfigJobBeta, ImportOptionsBeta, SpConfigExportResultsBeta, ObjectExportImportOptionsBeta, ExportPayloadBetaIncludeTypesEnum, TransformRead, GovernanceGroupsBetaApi, WorkgroupDtoBeta, AccessProfilesApi, AccessProfilesApiListAccessProfilesRequest, AccessProfile, RolesApi, Role, RolesApiListRolesRequest, Search, SearchApi, IdentityDocument, SearchDocument, AccessProfileDocument, EntitlementDocument, EntitlementBeta, RoleDocument, SourcesBetaApi, StatusResponseBeta, Schema, FormBeta, CustomFormsBetaApi, ExportFormDefinitionsByTenant200ResponseInnerBeta, FormDefinitionResponseBeta, NotificationsBetaApi, TemplateDtoBeta, SegmentsApi, Segment, SODPolicyApi, SodPolicy, SearchAttributeConfigurationBetaApi, SearchAttributeConfigBeta, IdentityAttributesBetaApi, IdentityAttributeBeta, PasswordConfigurationApi, PasswordOrgConfig, PasswordManagementBetaApi, ConnectorRuleUpdateRequestBeta, IdentitiesBetaApi, IdentitiesBetaApiListIdentitiesRequest, IdentityBeta, IdentitySyncJobBeta, TaskResultResponseBeta } from 'sailpoint-api-client'; import { DEFAULT_PUBLIC_IDENTITIES_QUERY_PARAMS } from '../models/PublicIdentity'; import axios, { AxiosInstance, AxiosResponse } from 'axios'; import { ImportEntitlementsResult } from '../models/JobStatus'; @@ -582,6 +582,25 @@ export class ISCClient { return await this.paginatedSearch(search, limit, offset, count); } + public async paginatedSearchIdentities(query: string, limit?: number, offset?: number, count = false, fields = ["id", "name"], includeNested = false): Promise> { + console.log("> paginatedSearchIdentities", query); + + const search: Search = { + indices: [ + "identities" + ], + query: { + query: query + }, + sort: ["name"], + includeNested, + queryResultFilter: { + includes: fields + } + }; + + return await this.paginatedSearch(search, limit, offset, count); + } public async paginatedSearch(query: Search, limit?: number, offset?: number, count?: boolean): Promise> { console.log("> paginatedSearch", query); @@ -1544,6 +1563,59 @@ export class ISCClient { ///////////////////////// //#endregion Password Management ///////////////////////// + + //////////////////////// + //#region Identity Management + //////////////////////// + + public async listIdentities(identityFilter: IdentitiesBetaApiListIdentitiesRequest): Promise> { + console.log("> listIdentities"); + const apiConfig = await this.getApiConfiguration(); + const api = new IdentitiesBetaApi(apiConfig); + const result = await api.listIdentities(identityFilter); + return result; + } + + public async processIdentity(identityId: string): Promise> { + console.log("> processIdentity"); + const apiConfig = await this.getApiConfiguration(); + const api = new IdentitiesBetaApi(apiConfig); + const requestParameters = { + processIdentitiesRequestBeta: + { + identityIds: [identityId] + } + }; + return await api.startIdentityProcessing(requestParameters); + } + + public async syncIdentityAttributes(identityId: string): Promise> { + console.log("> syncIdentityAttributes"); + + const apiConfig = await this.getApiConfiguration(); + const api = new IdentitiesBetaApi(apiConfig); + + //IdentitiesBetaApiSynchronizeAttributesForIdentityRequest + return await api.synchronizeAttributesForIdentity( + { + identityId: identityId + }); + } + + public async deleteIdentity(identityId: string): Promise { + console.log("> deleteIdentity"); + + const apiConfig = await this.getApiConfiguration(); + const api = new IdentitiesBetaApi(apiConfig); + + await api.deleteIdentity({ + id: identityId + }); + } + + //////////////////////// + //#endregion Identity Management + //////////////////////// } export enum AggregationJob {