mirror of
https://github.com/LukeHagar/sailpoint-cli.git
synced 2025-12-06 04:21:15 +00:00
221 lines
7.0 KiB
Go
221 lines
7.0 KiB
Go
// Copyright (c) 2021, SailPoint Technologies, Inc. All rights reserved.
|
|
package transform
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
|
|
transmodel "github.com/sailpoint-oss/sailpoint-cli/cmd/transform/model"
|
|
"github.com/sailpoint-oss/sailpoint-cli/internal/client"
|
|
"github.com/sailpoint-oss/sailpoint-cli/internal/config"
|
|
"github.com/sailpoint-oss/sailpoint-cli/internal/util"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var implicitInput bool
|
|
|
|
func newPreviewCommand() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "preview",
|
|
Short: "Preview the effects of an IdentityNow transform",
|
|
Long: "\nPreview the effects of an IdentityNow transform\n\n",
|
|
Example: "sail transform preview -i 020523e172fb4448b0814643ae52ff32 -a lastname -n ToLower --implicit\nsail transform preview -i 020523e172fb4448b0814643ae52ff32 -a lastname -n ToUpper --implicit\nsail transform p -i 12a199b967b64ffe992ef4ecfd076728 -a lastname -f /path/to/transform.json\nsail transform p -i 12a199b967b64ffe992ef4ecfd076728 -a lastname -n ToLower --implicit",
|
|
Aliases: []string{"p"},
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
idProfile := cmd.Flags().Lookup("identity-profile").Value.String()
|
|
attribute := cmd.Flags().Lookup("attribute").Value.String()
|
|
|
|
Config, err := config.GetConfig()
|
|
if err != nil {
|
|
cobra.CheckErr(err)
|
|
}
|
|
|
|
Client := client.NewSpClient(Config)
|
|
|
|
var transform map[string]interface{}
|
|
|
|
if !implicitInput {
|
|
filepath := cmd.Flags().Lookup("file").Value.String()
|
|
if filepath != "" {
|
|
file, err := os.Open(filepath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
err = json.NewDecoder(file).Decode(&transform)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
err := json.NewDecoder(os.Stdin).Decode(&transform)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the identity profile so we can obtain the authoritative source and
|
|
// original transform for the attribute, which will contain the account attribute
|
|
// name and source name that will be used in the preview body.
|
|
endpoint := cmd.Flags().Lookup("identity-profile-endpoint").Value.String()
|
|
resp, err := Client.Get(cmd.Context(), util.ResourceUrl(endpoint, idProfile))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func(Body io.ReadCloser) {
|
|
_ = Body.Close()
|
|
}(resp.Body)
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("get identity profile failed. status: %s\nbody: %s", resp.Status, body)
|
|
}
|
|
|
|
raw, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var profile transmodel.IdentityProfile
|
|
err = json.Unmarshal(raw, &profile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get a list of users in the source specified by the identity profile.
|
|
// These users will be used to preview the transform.
|
|
endpoint = cmd.Flags().Lookup("user-endpoint").Value.String()
|
|
uri, err := url.Parse(endpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
query := &url.Values{}
|
|
query.Add("filters", "[{\"property\":\"links.application.id\",\"operation\":\"EQ\",\"value\":\""+profile.AuthoritativeSource.Id+"\"}]")
|
|
uri.RawQuery = query.Encode()
|
|
|
|
resp, err = Client.Get(cmd.Context(), uri.String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func(Body io.ReadCloser) {
|
|
_ = Body.Close()
|
|
}(resp.Body)
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("get users failed. status: %s\nbody: %s", resp.Status, body)
|
|
}
|
|
|
|
raw, err = io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var user []transmodel.User
|
|
err = json.Unmarshal(raw, &user)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var previewBodyRaw []byte
|
|
// If using implicit input, then attempt to grab the implicit
|
|
// input from the identity profile mapping.
|
|
if implicitInput {
|
|
var accountAttName string
|
|
var sourceName string
|
|
for _, t := range profile.IdentityAttributeConfig.AttributeTransforms {
|
|
if t.IdentityAttributeName == attribute {
|
|
transType := t.TransformDefinition.Type
|
|
if transType == "accountAttribute" {
|
|
def := transmodel.MakeAttributesOfAccount(t.TransformDefinition.Attributes)
|
|
accountAttName = def.AttributeName
|
|
sourceName = def.SourceName
|
|
} else if transType == "reference" {
|
|
def := transmodel.MakeReference(t.TransformDefinition.Attributes)
|
|
accountAttName = def.Input.Attributes.AttributeName
|
|
sourceName = def.Input.Attributes.SourceName
|
|
} else {
|
|
return fmt.Errorf("unknown transform definition encountered when parsing identity profile: " + transType)
|
|
}
|
|
}
|
|
}
|
|
|
|
name := cmd.Flags().Lookup("name").Value.String()
|
|
if name == "" {
|
|
return fmt.Errorf("the transform name must be specified when previewing with implicit input")
|
|
}
|
|
|
|
previewBody := transmodel.MakePreviewBodyImplicit(attribute, name, accountAttName, sourceName)
|
|
|
|
previewBodyRaw, err = json.Marshal(previewBody)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
previewBody := transmodel.MakePreviewBodyExplicit(attribute, transform)
|
|
|
|
previewBodyRaw, err = json.Marshal(previewBody)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Call the preview endpoint to get the raw and transformed attribute values
|
|
endpoint = cmd.Flags().Lookup("preview-endpoint").Value.String()
|
|
resp, err = Client.Post(cmd.Context(), util.ResourceUrl(endpoint, user[0].Id), "application/json", bytes.NewReader(previewBodyRaw))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func(Body io.ReadCloser) {
|
|
_ = Body.Close()
|
|
}(resp.Body)
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("preview transform failed. status: %s\nbody: %s", resp.Status, body)
|
|
}
|
|
|
|
raw, err = io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var response transmodel.PreviewResponse
|
|
err = json.Unmarshal(raw, &response)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, x := range response.PreviewAttributes {
|
|
if x.Name == attribute {
|
|
fmt.Printf("Original value: %s\nTransformed value: %s\n", x.PreviousValue, x.Value)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
cmd.Flags().StringP("identity-profile", "i", "", "GUID of an identity profile (required)")
|
|
cmd.Flags().StringP("attribute", "a", "", "Attribute name (required)")
|
|
cmd.Flags().StringP("name", "n", "", "Transform name. Only needed if using implicit input. The transform must be uploaded to IDN first.")
|
|
cmd.Flags().BoolVar(&implicitInput, "implicit", false, "Use implicit input. Default is explicit input defined by the transform.")
|
|
cmd.Flags().String("preview-endpoint", previewEndpoint, "Override preview endpoint")
|
|
cmd.Flags().String("identity-profile-endpoint", identityProfileEndpoint, "Override identity profile endpoint")
|
|
cmd.Flags().String("user-endpoint", userEndpoint, "Override user endpoint")
|
|
cmd.Flags().StringP("file", "f", "", "Path to the transform file. Only needed if using explicit input.")
|
|
|
|
cmd.MarkFlagRequired("identity-profile")
|
|
cmd.MarkFlagRequired("attribute")
|
|
|
|
return cmd
|
|
}
|