Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const connector = async () => {
const airtable = new AirtableClient(config)

return createConnector()
.stdAccountList(async (context: Context, input: undefined, res: Response<StdAccountListOutput>) => {
.stdAccountList(async (context: Context, input: StdAccountListInput, res: Response<StdAccountListOutput>) => {
const accounts = await airtable.getAllAccounts()

for (const account of accounts) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
id: application-discovery-list
title: Application Discovery List
pagination_label: Application Discovery List
sidebar_label: Application Discovery List
keywords: ['connectivity', 'connectors', 'application discovery']
description: Return the applications that the source knows about.
slug: /connectivity/saas-connectivity/commands/application-discovery-list
tags: ['Connectivity', 'Connector Command']
---

| Input/Output | Data Type |
| :----------- | :------------------------------------------: |
| Input | StdApplicationDiscoveryListInput |
| Output | StdApplicationDiscoveryListOutput |

### Example StdApplicationDiscoveryListInput

```javascript
{
"datasetId": "sso-okta:applications"
}
```

### Example StdApplicationDiscoveryListOutput

```javascript
{
"id": "app-5678",
"primaryAppName": "Slack",
"description": "Team messaging",
"status": "ACTIVE",
"connectorCategory": "sso",
"appUrl": "https://example.slack.com",
"usersCount": "250",
"riskLevel": "Low"
}
```

## Description

The application discovery list command returns the applications that the source knows about. For example, an identity provider (SSO) or a configuration management database (CMDB) tracks the apps that a company uses. SailPoint Identity Security shows the results in Application Discovery.

The tenant must have the Application Discovery license (`idn:application-discovery`).

To use this command, you must specify this value in the `commands` array: `std:application-discovery:list`

### Declare datasets in the connector spec

Add a `datasets` object to `connector-spec.json`. Each key is a dataset ID. Set `type` to `std:application_discovery`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you confirm std:application_discovery (with the underscore) is the correct dataset type? The command has a hyphen instead of an underscore. If it is, I would add a short note that it differs from the command name, since it will be easy to copy incorrectly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes this is actually correct.


```json
{
"commands": ["std:test-connection", "std:application-discovery:list"],
"datasets": {
"sso-okta:applications": {
"type": "std:application_discovery",
"name": "discovered-application",
"identityAttribute": "id",
"displayAttribute": "primaryAppName",
"schema": {
"type": "object",
"properties": {
"id": { "type": "string" },
"primaryAppName": { "type": "string" },
"description": { "type": "string" }
},
"required": ["id", "primaryAppName", "description"]
}
}
}
}
```

### When SailPoint Identity Security calls the command

An application discovery starts in one of these ways:

- An admin calls the [start application discovery API](/docs/api/start-application-discovery-v-1) with the dataset IDs. On-demand runs have a daily limit for each source.
- A source schedule of type `APPLICATION_DISCOVERY` starts it. The schedule has a cron expression and the dataset IDs:

```json
{
"type": "APPLICATION_DISCOVERY",
"cronExpression": "15 * * * *",
"config": {
"datasetIds": ["sso-okta:applications"]
}
}
```

SailPoint Identity Security sends `input.datasetIds` with the datasets to discover. SailPoint Identity Security can also send `additionalParameters` to pass extra data to some SailPoint connectors, for example the location of a file to read. Your connector does not need to use it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are datasetId and datasetIds the same field? This line says SailPoint Identity Security sends input.datasetIds, but the input example shows datasetId and the code reads input.datasetId.

From what I can tell, the platform sends datasetIds (a list), and with stdApplicationDiscoveryListWithDataset the SDK calls your handler once per ID, with that single ID in input.datasetId. If that's right, I'd spell it out here, just in case!


If an application is missing from two discovery runs in a row, SailPoint Identity Security sets its status to `INACTIVE`.

### Output fields

Each application needs `id`, `primaryAppName`, and `description`. All other fields are optional.

| Field | Values |
| :---- | :----- |
| `status` | A string, for example `ACTIVE` or `INACTIVE`. |
| `connectorCategory` | The kind of source, for example `sso` or `cmdb`. |
| `riskLevel` | `High`, `Medium`, or `Low`. |
| `riskScore` | A number. |
| `usersCount` | A string. Convert numbers with `String()`. |

The application list in SailPoint Identity Security shows the name, description, category, discovery connector, business criticality, status, and the first and last discovery dates. The details page of each application shows the other fields.

## Implementation

The SDK has two ways to register this command, but only one can be used at a time.

| Method | Use it when |
| :----- | :---------- |
| `stdApplicationDiscoveryListWithDataset` | Your connector declares datasets. This is the method for new connectors. |
| `stdApplicationDiscoveryList` | Your connector has no datasets. The input is an empty object. |

With `stdApplicationDiscoveryListWithDataset`, the SDK calls your handler once for each dataset ID and adds `datasetId` to each object that you send.

Call `res.send()` once for each application.

```typescript
import {
Context,
createConnector,
readConfig,
Response,
StdApplicationDiscoveryListInput,
StdApplicationDiscoveryListOutput,
} from '@sailpoint/connector-sdk'
import { MyClient } from './my-client'

export const connector = async () => {
const config = await readConfig()
const client = new MyClient(config)

return createConnector()
.stdApplicationDiscoveryListWithDataset(async (context: Context, input: StdApplicationDiscoveryListInput, res: Response<StdApplicationDiscoveryListOutput>) => {
const apps: { id: string; name: string; description: string; url: string; userCount: number; active: boolean }[] =
await client.getApplications(input.datasetId)
for (const app of apps) {
res.send({
id: app.id,
primaryAppName: app.name,
description: app.description,
status: app.active ? 'ACTIVE' : 'INACTIVE',
connectorCategory: 'sso',
appUrl: app.url,
usersCount: String(app.userCount),
})
}
})
}
```
125 changes: 125 additions & 0 deletions docs/connectivity/saas-connectivity/connector-commands/authenticate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
id: authenticate
title: Authenticate
pagination_label: Authenticate
sidebar_label: Authenticate
keywords: ['connectivity', 'connectors', 'authenticate', 'pass-through authentication']
description: Check a user's credentials on the source for pass-through authentication.
slug: /connectivity/saas-connectivity/commands/authenticate
tags: ['Connectivity', 'Connector Command']
---

| Input/Output | Data Type |
| :----------- | :---------------------: |
| Input | StdAuthenticateInput |
| Output | StdAuthenticateOutput |

### Example StdAuthenticateInput

```javascript
{
"identity": "john.doe",
"key": {
"simple": {
"id": "john.doe"
}
},
"username": "john.doe",
"password": "<password>",
"schema": {
"displayAttribute": "displayName",
"identityAttribute": "id",
"groupAttribute": "groups",
"attributes": [
{
"name": "id",
"description": "Unique ID",
"type": "string"
}
]
}
}
```

### Example StdAuthenticateOutput

```javascript
{
"identity": "john.doe",
"key": {
"simple": {
"id": "john.doe"
}
},
"disabled": false,
"locked": false,
"attributes": {
"id": "john.doe",
"displayName": "John Doe",
"email": "example@sailpoint.com"
}
}
```

## Description

The authenticate command supports pass-through authentication. With pass-through authentication, users sign in to SailPoint Identity Security with their username and password from the source. SailPoint Identity Security does not store these passwords. It sends them to the connector, and the connector tests them against the source.

If the admin sets the sign-in method to **Directory Connection** with this source, SailPoint Identity Security sends `std:authenticate` each time a user signs in. The connector must do these steps:

1. Test the username and password against the source.
2. If the credentials are valid, get the user's account from the source.
3. Send the account to SailPoint Identity Security. SailPoint Identity Security then lets the user sign in.

If the credentials are not valid, throw a `ConnectorError`. SailPoint Identity Security then rejects the sign-in.

The output has the same shape as the account read output, and it can also include `permissions`. The input includes the account schema of the source in `input.schema`. Use it to decide which attributes to return.

To use this command, you must specify this value in the `commands` array: `std:authenticate`

### Disabled and locked accounts

Let the source decide whether a disabled or locked user can sign in. If the source accepts the credentials, send the account. Set `disabled` and `locked` to the values from the source.

### Usernames

The username that reaches the connector depends on what the user types and on the account correlation of the identity profile. If the user cannot sign in, and the connector logs show no `std:authenticate` command, look at the correlation settings first.

## Implementation

```typescript
import {
ConnectorError,
Context,
createConnector,
readConfig,
Response,
StdAuthenticateInput,
StdAuthenticateOutput,
} from '@sailpoint/connector-sdk'
import { MyClient } from './my-client'

export const connector = async () => {
const config = await readConfig()
const client = new MyClient(config)

return createConnector()
.stdAuthenticate(async (context: Context, input: StdAuthenticateInput, res: Response<StdAuthenticateOutput>) => {
const account = await client.authenticate(input.username, input.password)
if (!account) {
throw new ConnectorError(`Authentication failed for ${input.username}`)
}
res.send({
identity: account.id,
disabled: !account.active,
locked: account.locked,
attributes: {
id: account.id,
displayName: account.displayName,
email: account.email,
},
})
})
}
```

Loading
Loading