From e3c207683b4b9fea63cddf5f989a863f82631f13 Mon Sep 17 00:00:00 2001 From: Steve Galambos Date: Sun, 5 Nov 2023 11:11:59 -0500 Subject: [PATCH 1/2] add data_source_okta_apps --- okta/app_filter.go | 1 + okta/data_source_okta_apps.go | 220 ++++++++++++++++++++++++++++++++++ okta/framework_provider.go | 1 + okta/provider.go | 1 + 4 files changed, 223 insertions(+) create mode 100644 okta/data_source_okta_apps.go diff --git a/okta/app_filter.go b/okta/app_filter.go index 8f6ab8b6a..57c9fed54 100644 --- a/okta/app_filter.go +++ b/okta/app_filter.go @@ -67,3 +67,4 @@ func getAppFilters(d *schema.ResourceData) (*appFilters, error) { } return filters, nil } + diff --git a/okta/data_source_okta_apps.go b/okta/data_source_okta_apps.go new file mode 100644 index 000000000..3af13b0ec --- /dev/null +++ b/okta/data_source_okta_apps.go @@ -0,0 +1,220 @@ +package okta + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/okta/okta-sdk-golang/v5/okta" +) + +var _ datasource.DataSource = &AppsDataSource{} + +func NewAppsDataSource() datasource.DataSource { return &AppsDataSource{} } + +type AppsDataSource struct{ config *Config } + +type AppsDataSourceModel struct { + ActiveOnly types.Bool `tfsdk:"active_only"` + Label types.String `tfsdk:"label"` + LabelPrefix types.String `tfsdk:"label_prefix"` + IncludeNonDeleted types.Bool `tfsdk:"include_non_deleted"` + UseOptimization types.Bool `tfsdk:"use_optimization"` + Apps []OktaAppModel `tfsdk:"apps"` +} + +type OktaAppModel struct { + ID types.String `tfsdk:"id"` + Created types.String `tfsdk:"created"` + LastUpdated types.String `tfsdk:"last_updated"` + Name types.String `tfsdk:"name"` + Label types.String `tfsdk:"label"` + Status types.String `tfsdk:"status"` + SignOnMode types.String `tfsdk:"sign_on_mode"` + Features types.List `tfsdk:"features"` + AdminNote types.String `tfsdk:"admin_note"` + EndUserNote types.String `tfsdk:"enduser_note"` +} + +type OktaApp interface { + GetId() string + GetCreated() time.Time + GetLastUpdated() time.Time + GetName() string + GetLabel() string + GetStatus() string + GetSignOnMode() string + GetFeatures() []string +} + +func (d *AppsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_apps" +} + +func (d *AppsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "active_only": schema.BoolAttribute{ + Optional: true, + Description: "Search only active applications.", + }, + "label": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{stringvalidator.ConflictsWith(path.MatchRoot("label_prefix"))}, + Description: "Searches for applications whose label or name property matches this value exactly.", + }, + "label_prefix": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{stringvalidator.ConflictsWith(path.MatchRoot("label"))}, + Description: "Searches for applications whose label or name property begins with this value.", + }, + "include_non_deleted": schema.BoolAttribute{ + Optional: true, + Description: "Specifies whether to include non-active, but not deleted apps in the results.", + }, + "use_optimization": schema.BoolAttribute{ + Optional: true, + Description: "Specifies whether to use query optimization. If you specify `useOptimization=true` in the request query, the response contains a subset of app instance properties.", + }, + "apps": schema.ListAttribute{ + Description: "The list of applications that match the search criteria.", + Computed: true, + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "created": types.StringType, + "last_updated": types.StringType, + "name": types.StringType, + "label": types.StringType, + "status": types.StringType, + "sign_on_mode": types.StringType, + "features": types.ListType{ElemType: types.StringType}, + "admin_note": types.StringType, + "enduser_note": types.StringType, + }, + }, + }, + }, + } +} + +func (d *AppsDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + d.config = dataSourceConfiguration(req, resp) +} + +func (d *AppsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var state AppsDataSourceModel + resp.Diagnostics.Append(req.Config.Get(ctx, &state)...) + + var filters []string + if state.ActiveOnly == types.BoolValue(true) { + filters = append(filters, `status eq "ACTIVE"`) + } + if label := state.Label.ValueString(); label != "" { + filters = append(filters, fmt.Sprintf(`label eq "%s"`, label)) + } else if labelPrefix := state.LabelPrefix.ValueString(); labelPrefix != "" { + filters = append(filters, fmt.Sprintf(`label sw "%s"`, labelPrefix)) + } + filterValue := strings.Join(filters, " AND ") + + // Read the list of applications from Okta. + apiRequest := d.config.oktaSDKClientV5.ApplicationAPI.ListApplications(ctx).Filter(filterValue) + applicationList, apiResp, err := apiRequest.Execute() + if err != nil { + resp.Diagnostics.AddError("Unable to Read Okta Apps", fmt.Sprintf("Error retrieving apps: %s", err.Error())) + return + } + // Handle api pagination + for apiResp.HasNextPage() { + var nextApps []okta.ListApplications200ResponseInner + apiResp, err = apiResp.Next(&nextApps) + if err != nil { + resp.Diagnostics.AddError( + "Unable to Read Okta Apps", + fmt.Sprintf("Error retrieving next page of apps: %s", err.Error()), + ) + return + } + applicationList = append(applicationList, nextApps...) + } + + // Convert the list of applications to the Terraform schema. + for _, app := range applicationList { + oktaApp := app.GetActualInstance().(OktaApp) + + FeaturesList, err := types.ListValueFrom(ctx, types.StringType, oktaApp.GetFeatures()) + resp.Diagnostics.Append(err...) + + adminNotes, userNotes := getNotes(oktaApp) + + state.Apps = append(state.Apps, OktaAppModel{ + ID: types.StringValue(oktaApp.GetId()), + Created: types.StringValue(oktaApp.GetCreated().Format(time.RFC3339)), + LastUpdated: types.StringValue(oktaApp.GetLastUpdated().Format(time.RFC3339)), + Name: types.StringValue(oktaApp.GetName()), + Label: types.StringValue(oktaApp.GetLabel()), + Status: types.StringValue(oktaApp.GetStatus()), + SignOnMode: types.StringValue(oktaApp.GetSignOnMode()), + Features: FeaturesList, + AdminNote: types.StringValue(adminNotes), + EndUserNote: types.StringValue(userNotes), + }) + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func getNotesFromSettings(notes *okta.ApplicationSettingsNotes) (string, string) { + if notes == nil { + return "", "" + } + + admin := "" + if notes.Admin != nil { + admin = *notes.Admin + } + + user := "" + if notes.Enduser != nil { + user = *notes.Enduser + } + + return admin, user +} + +func getNotes(app interface{}) (string, string) { + if app == nil { + return "", "" + } + + switch v := app.(type) { + case *okta.AutoLoginApplication: + return getNotesFromSettings(v.Settings.Notes) + case *okta.BasicAuthApplication: + return getNotesFromSettings(v.Settings.Notes) + case *okta.BookmarkApplication: + return getNotesFromSettings(v.Settings.Notes) + case *okta.BrowserPluginApplication: + return getNotesFromSettings(v.Settings.Notes) + case *okta.OpenIdConnectApplication: + return getNotesFromSettings(v.Settings.Notes) + case *okta.Saml11Application: + return getNotesFromSettings(v.Settings.Notes) + case *okta.SamlApplication: + return getNotesFromSettings(v.Settings.Notes) + case *okta.SecurePasswordStoreApplication: + return getNotesFromSettings(v.Settings.Notes) + case *okta.WsFederationApplication: + return getNotesFromSettings(v.Settings.Notes) + } + + return "", "" +} diff --git a/okta/framework_provider.go b/okta/framework_provider.go index 0c82c8190..31909a206 100644 --- a/okta/framework_provider.go +++ b/okta/framework_provider.go @@ -250,6 +250,7 @@ func (p *FrameworkProvider) DataSources(_ context.Context) []func() datasource.D NewOrgMetadataDataSource, NewDefaultSigninPageDataSource, NewLogStreamDataSource, + NewAppsDataSource, } } diff --git a/okta/provider.go b/okta/provider.go index 72c28f4a7..5899a5bac 100644 --- a/okta/provider.go +++ b/okta/provider.go @@ -21,6 +21,7 @@ const ( adminRoleCustomAssignments = "okta_admin_role_custom_assignments" adminRoleTargets = "okta_admin_role_targets" app = "okta_app" + apps = "okta_apps" appAutoLogin = "okta_app_auto_login" appBasicAuth = "okta_app_basic_auth" appBookmark = "okta_app_bookmark" From 651d9ba2d8589c0ad37b0a46be312b2d3fb8750d Mon Sep 17 00:00:00 2001 From: Tien Nguyen Date: Tue, 12 Nov 2024 13:25:23 -0500 Subject: [PATCH 2/2] add visibility Signed-off-by: Tien Nguyen --- okta/data_source_okta_apps.go | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/okta/data_source_okta_apps.go b/okta/data_source_okta_apps.go index 3af13b0ec..11ba2e01f 100644 --- a/okta/data_source_okta_apps.go +++ b/okta/data_source_okta_apps.go @@ -42,6 +42,18 @@ type OktaAppModel struct { Features types.List `tfsdk:"features"` AdminNote types.String `tfsdk:"admin_note"` EndUserNote types.String `tfsdk:"enduser_note"` + Visibility types.Object `tfsdk:"visibility"` +} + +type OktaAppVisibility struct { + AutoLaunch types.Bool `tfsdk:"auto_launch"` + AutoSubmitToolbar types.Bool `tfsdk:"auto_submit_toolbar"` + Hide types.Object `tfsdk:"hide"` +} + +type OktaAppVisibilityHide struct { + IOS types.Bool `tfsdk:"ios"` + Web types.Bool `tfsdk:"web"` } type OktaApp interface { @@ -53,6 +65,7 @@ type OktaApp interface { GetStatus() string GetSignOnMode() string GetFeatures() []string + GetVisibility() okta.ApplicationVisibility } func (d *AppsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { @@ -99,6 +112,18 @@ func (d *AppsDataSource) Schema(ctx context.Context, req datasource.SchemaReques "features": types.ListType{ElemType: types.StringType}, "admin_note": types.StringType, "enduser_note": types.StringType, + "visibility": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "auto_launch": types.BoolType, + "auto_submit_toolbar": types.BoolType, + "hide": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "ios": types.BoolType, + "web": types.BoolType, + }, + }, + }, + }, }, }, }, @@ -155,6 +180,35 @@ func (d *AppsDataSource) Read(ctx context.Context, req datasource.ReadRequest, r adminNotes, userNotes := getNotes(oktaApp) + hideValue := map[string]attr.Value{ + "ios": types.BoolPointerValue(oktaApp.GetVisibility().Hide.IOS), + "web": types.BoolPointerValue(oktaApp.GetVisibility().Hide.Web), + } + hideTypes := map[string]attr.Type{ + "ios": types.BoolType, + "web": types.BoolType, + } + + hide, _ := types.ObjectValue(hideTypes, hideValue) + + visibilityValue := map[string]attr.Value{ + "hide": hide, + "auto_launch": types.BoolPointerValue(oktaApp.GetVisibility().AutoLaunch), + "auto_submit_toolbar": types.BoolPointerValue(oktaApp.GetVisibility().AutoSubmitToolbar), + } + visibilityTypes := map[string]attr.Type{ + "hide": types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "ios": types.BoolType, + "web": types.BoolType, + }, + }, + "auto_launch": types.BoolType, + "auto_submit_toolbar": types.BoolType, + } + + visibility, _ := types.ObjectValue(visibilityTypes, visibilityValue) + state.Apps = append(state.Apps, OktaAppModel{ ID: types.StringValue(oktaApp.GetId()), Created: types.StringValue(oktaApp.GetCreated().Format(time.RFC3339)), @@ -166,6 +220,7 @@ func (d *AppsDataSource) Read(ctx context.Context, req datasource.ReadRequest, r Features: FeaturesList, AdminNote: types.StringValue(adminNotes), EndUserNote: types.StringValue(userNotes), + Visibility: visibility, }) }