identitynow

package
v1.86.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: BSD-3-Clause Imports: 22 Imported by: 0

README

SailPoint IdentityNow Adapter

Entity External IDs

The Entity External ID is used directly as the API path segment. The adapter constructs URLs as:

{baseURL}/{apiVersion}/{entityExternalID}?limit={pageSize}&offset={offset}

The adapter is open-ended - any SailPoint IdentityNow API resource name can be used as an entity external ID.

Entity External ID API Path Description
accounts /{apiVersion}/accounts SailPoint accounts
entitlements /{apiVersion}/entitlements Standalone entitlements
accountEntitlements /{apiVersion}/accounts/{accountID}/entitlements Entitlements scoped to a specific account (special case)

Special Cases

accountEntitlements is the only entity with custom URL construction. Instead of being appended directly as a path segment, it decomposes into a sub-resource pattern: /accounts/{accountID}/entitlements.

All other entity external IDs (e.g., roles, identities, access-profiles) are placed directly into the URL path.

Supported API Versions

  • v3
  • beta

Each entity can optionally override the default API version via its entity configuration.

Adding New Entity External IDs

No code changes required for standard entities. The adapter appends any entity external ID directly to the URL path. Simply configure a new entity in SGNL with the SailPoint resource name (e.g., roles, identities, access-profiles) and it will work.

To add a new entity with custom URL construction (like accountEntitlements):

  1. Add a constant in datasource.go.
  2. Add a case in ConstructEndpoint in endpoint.go with the custom URL pattern.
  3. If the entity is a sub-resource (like account entitlements), implement collection-based pagination to iterate through the parent resource.
  4. Add tests.

Documentation

Index

Constants

View Source
const (
	AccountEntitlements              = "accountEntitlements"
	Accounts                         = "accounts"
	DefaultAccountCollectionPageSize = 100

	// TODO [sc-19214]: Remove after POC complete.
	Delimiter string = " | "
)
View Source
const (
	// MaxPageSize is the maximum page size allowed in a GetPage request.
	// The IdentityNow documentation specifies a page size limit of 250.
	// https://developer.sailpoint.com/idn/api/standard-collection-parameters/#paginating-results.
	MaxPageSize = 250
)

Variables

View Source
var (
	// TODO [sc-19214]: Remove after POC complete.
	AttributesToConcatenate = []string{"groups", "Groups", "memberOf"}
	DefaultAccountSorter    = "id"
	Validate                = validator.New(validator.WithRequiredStructEnabled())
)

Functions

func ConstructEndpoint

func ConstructEndpoint(request *Request) (string, *framework.Error)

ConstructEndpoint constructs and returns the endpoint to query the datasource.

func NewAdapter

func NewAdapter(client Client) framework.Adapter[Config]

NewAdapter instantiates a new Adapter.

func ParseResponse

func ParseResponse(body []byte) (objects []map[string]any, err *framework.Error)

TODO: This is identical to the ParseResponse in the Okta package. Refactor into a shared "parser" package.

Types

type AccountObject

type AccountObject struct {
	AccountID       string `mapstructure:"id" validate:"required"`
	HasEntitlements bool   `mapstructure:"hasEntitlements" validate:"omitempty"`
}

type Adapter

type Adapter struct {
	IdentityNowClient Client
}

Adapter implements the framework.Adapter interface to query pages of objects from datasources.

func (*Adapter) GetPage

func (a *Adapter) GetPage(ctx context.Context, request *framework.Request[Config]) framework.Response

GetPage is called by SGNL's ingestion service to query a page of objects from a datasource.

func (*Adapter) RequestPageFromDatasource

func (a *Adapter) RequestPageFromDatasource(
	ctx context.Context, request *framework.Request[Config],
) framework.Response

RequestPageFromDatasource requests a page of objects from a datasource.

func (*Adapter) ValidateGetPageRequest

func (a *Adapter) ValidateGetPageRequest(ctx context.Context, request *framework.Request[Config]) *framework.Error

ValidateGetPageRequest validates the fields of the GetPage Request.

type Client

type Client interface {
	GetPage(ctx context.Context, request *Request) (*Response, *framework.Error)
}

Client is a client that allows querying the IdentityNow datasource which contains JSON objects.

func NewClient

func NewClient(client *http.Client, pageSize int) Client

NewClient returns a Client to query the datasource.

type Config

type Config struct {
	*config.CommonConfig

	// APIVersion is the default API version to use for a request. Required.
	APIVersion string `json:"apiVersion,omitempty"`

	// EntityConfig is a map of configs for each entity associated with this datasource.
	// The key is the entity's external ID, and the value is a map of config values.
	EntityConfig map[string]EntityConfig `json:"entityConfig,omitempty"`
}

Config is the configuration passed in each GetPage calls to the adapter. Adapter configuration example: nolint: godot

{
	"requestTimeoutSeconds": 10,
	"localTimeZoneOffset": 43200,
	"apiVersion": "v3",
	"entityConfig": {
		"accounts": {
			"uniqueIDAttribute": "id",
			"filter": "identityId eq \"1700926aca594aa2861d9dbd24ca64b9\"",
			"apiVersion": "v3"
		}
	}
}

func (*Config) Validate

func (c *Config) Validate(_ context.Context) error

ValidateConfig validates that a Config received in a GetPage call is valid.

type Datasource

type Datasource struct {
	Client                    *http.Client
	AccountCollectionPageSize int
}

Datasource directly implements a Client interface to allow querying an external datasource.

func (*Datasource) ConstructEndpointAndGetResponse

func (d *Datasource) ConstructEndpointAndGetResponse(
	ctx context.Context,
	request *Request,
) (*Response, []map[string]any, *framework.Error)

ConstructEndpointAndGetResponse constructs the endpoint and returns the response from the datasource.

func (*Datasource) GetPage

func (d *Datasource) GetPage(ctx context.Context, request *Request) (*Response, *framework.Error)

type DatasourceResponse

type DatasourceResponse = []map[string]any

DatasouceResponse represents the API response from the datasource, which is an array of objects.

type EntityConfig

type EntityConfig struct {
	// UniqueIDAttribute is the name of the attribute containing the unique ID of
	// each returned object for the requested entity. Required.
	UniqueIDAttribute string `json:"uniqueIDAttribute"`
	// Filter is the filter to apply to the entity request. Optional.
	Filter *string `json:"filter,omitempty"`
	// APIVersion is the API version to use for the entity request. Optional.
	// If set, overrides the default Config.APIVersion.
	APIVersion *string `json:"apiVersion,omitempty"`
}

type Request

type Request struct {
	// BaseURL is the Base URL of the datasource to query.
	// For example, https://{tenant}.api.identitynow.com.
	BaseURL string

	// Token is the API token to authenticate a request. IdentityNow supports OAuth2 Client Credential auth
	// so this must be in the form "Bearer eyJhbG[...]1LQ".
	Token string

	// PageSize is the maximum number of objects to return from the entity.
	PageSize int64

	// EntityExternalID is the external ID of the entity.
	// The external ID should match the API's resource name.
	EntityExternalID string

	// Cursor identifies the first object of the page to return, as returned by
	// the last request for the entity.
	// nil in the request for the first page.
	Cursor *pagination.CompositeCursor[int64]

	// APIVersion the API version to use.
	APIVersion string

	// RequestTimeoutSeconds is the timeout duration for requests made to datasources.
	// This should be set to the number of seconds to wait before timing out.
	RequestTimeoutSeconds int

	// Filter contains the optional filter to apply to the current request.
	// It's applied using the `filters` query parameter.
	Filter *string

	// Sorters contains the optional sorters param to apply to the current request.
	// It contains a comma separated value of field names by which the results are sorted.
	//
	// Example: sorters=type,-modified
	// Results are sorted primarily by type in ascending order, and secondarily by modified date in descending order.
	Sorters *string
}

Request is a request to IdentityNow.

type Response

type Response struct {
	// StatusCode is an HTTP status code.
	StatusCode int

	// RetryAfterHeader is the Retry-After response HTTP header, if set.
	RetryAfterHeader string

	// Objects is the list of
	// May be empty.
	Objects []map[string]any

	// NextCursor is the cursor that identifies the first object of the next page.
	// nil if this is the last page in this full sync.
	NextCursor *pagination.CompositeCursor[int64]
}

Response is a response returned by the datasource.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL