From 30e3c67f4412af81cba85710db0b092119fa87e5 Mon Sep 17 00:00:00 2001 From: Mohammad Abudayyeh <47318409+moabu@users.noreply.github.com> Date: Thu, 16 Feb 2023 11:55:22 +0200 Subject: [PATCH] fix(terraform-provider-jans): update terraform module (#3869) * fix(terraform-provider-jans): update terraform provider Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> * ci: fix changlog --------- Signed-off-by: moabu <47318409+moabu@users.noreply.github.com> --- terraform-provider-jans/.goreleaser.yml | 60 ++++++++++ terraform-provider-jans/CHANGELOG.md | 21 +++- .../jans/admin_ui_rbac_test.go | 12 ++ .../jans/app_configuration.go | 6 +- .../jans/app_configuration_test.go | 14 ++- terraform-provider-jans/jans/client.go | 60 ++++++++++ terraform-provider-jans/jans/client_test.go | 77 +++++++++++- .../jans/custom_user_test.go | 3 +- .../jans/fido2_config_test.go | 4 + .../jans/fido2_configuration_test.go | 4 + .../jans/fido2_device_test.go | 4 + terraform-provider-jans/jans/group_test.go | 6 +- .../jans/oidc_client_test.go | 19 ++- terraform-provider-jans/jans/scripts_test.go | 11 +- terraform-provider-jans/jans/smtp_config.go | 32 ----- terraform-provider-jans/jans/user_test.go | 4 + .../provider/entity_mapper.go | 112 +++++++++++------- .../provider/resource_app_configuration.go | 47 +++++--- .../resource_app_configuration_test.go | 4 + .../terraform-registry-manifest.json | 6 + 20 files changed, 398 insertions(+), 108 deletions(-) create mode 100644 terraform-provider-jans/.goreleaser.yml create mode 100644 terraform-provider-jans/terraform-registry-manifest.json diff --git a/terraform-provider-jans/.goreleaser.yml b/terraform-provider-jans/.goreleaser.yml new file mode 100644 index 00000000000..4bb39e825c2 --- /dev/null +++ b/terraform-provider-jans/.goreleaser.yml @@ -0,0 +1,60 @@ +# Visit https://goreleaser.com for documentation on how to customize this +# behavior. +before: + hooks: + # this is just an example and not a requirement for provider building/publishing + - go mod tidy +builds: +- env: + # goreleaser does not work with CGO, it could also complicate + # usage by users in CI/CD systems like Terraform Cloud where + # they are unable to install libraries. + - CGO_ENABLED=0 + mod_timestamp: '{{ .CommitTimestamp }}' + flags: + - -trimpath + ldflags: + - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' + goos: + - freebsd + - windows + - linux + - darwin + goarch: + - amd64 + - '386' + - arm + - arm64 + ignore: + - goos: darwin + goarch: '386' + binary: '{{ .ProjectName }}_v{{ .Version }}' +archives: +- format: zip + name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' +checksum: + extra_files: + - glob: 'terraform-registry-manifest.json' + name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' + name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' + algorithm: sha256 +signs: + - artifacts: checksum + args: + # if you are using this in a GitHub action or some other automated pipeline, you + # need to pass the batch flag to indicate its not interactive. + - "--batch" + - "--local-user" + - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key + - "--output" + - "${signature}" + - "--detach-sign" + - "${artifact}" +release: + extra_files: + - glob: 'terraform-registry-manifest.json' + name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' + # If you want to manually examine the release before its live, uncomment this line: + # draft: true +changelog: + skip: true diff --git a/terraform-provider-jans/CHANGELOG.md b/terraform-provider-jans/CHANGELOG.md index 6d8e8fbfb1f..822f649f30a 100644 --- a/terraform-provider-jans/CHANGELOG.md +++ b/terraform-provider-jans/CHANGELOG.md @@ -1,8 +1,27 @@ # Changelog +## [0.2.0](https://github.com/JannsenProject/terraform-provider-jans/compare/v0.1.0...v0.2.0) (2023-02-16) + + +### Features + +* add initial janssen terraform provider + + +### Bug Fixes + +* 3 obsolete validations +* added sorting of parameter array for app configuration +* added support for nested slices as entity attributes +* adjusted validations to match latest API definition +* obsolete validations +* remove debugging code +* update attribute description to reflect new validation rules +* update description of app config to include info on sort order + ## 0.1.0 (2023-02-08) ### Features -* add initial janssen terraform provider +* add initial janssen terraform provider diff --git a/terraform-provider-jans/jans/admin_ui_rbac_test.go b/terraform-provider-jans/jans/admin_ui_rbac_test.go index b14df5d4dad..51be341368c 100644 --- a/terraform-provider-jans/jans/admin_ui_rbac_test.go +++ b/terraform-provider-jans/jans/admin_ui_rbac_test.go @@ -10,6 +10,10 @@ import ( func TestAdminUIRoles(t *testing.T) { + if skipKnownFailures { + t.SkipNow() + } + client, err := NewInsecureClient(host, user, pass) if err != nil { t.Fatal(err) @@ -68,6 +72,10 @@ func TestAdminUIRoles(t *testing.T) { func TestAdminUIPermissions(t *testing.T) { + if skipKnownFailures { + t.SkipNow() + } + client, err := NewInsecureClient(host, user, pass) if err != nil { t.Fatal(err) @@ -125,6 +133,10 @@ func TestAdminUIPermissions(t *testing.T) { func TestAdminUIRolePermissions(t *testing.T) { + if skipKnownFailures { + t.SkipNow() + } + client, err := NewInsecureClient(host, user, pass) if err != nil { t.Fatal(err) diff --git a/terraform-provider-jans/jans/app_configuration.go b/terraform-provider-jans/jans/app_configuration.go index e5b6f017c43..7f8a58ab2ba 100644 --- a/terraform-provider-jans/jans/app_configuration.go +++ b/terraform-provider-jans/jans/app_configuration.go @@ -186,8 +186,8 @@ type AppConfiguration struct { DynamicRegistrationCustomObjectClass string `schema:"dynamic_registration_custom_object_class" json:"dynamicRegistrationCustomObjectClass"` DynamicRegistrationScopesParamEnabled bool `schema:"dynamic_registration_scopes_param_enabled" json:"dynamicRegistrationScopesParamEnabled"` DynamicRegistrationPasswordGrantTypeEnabled bool `schema:"dynamic_registration_password_grant_type_enabled" json:"dynamicRegistrationPasswordGrantTypeEnabled"` - PersistIdToken bool `schema:"persist_id_token_in_ldap" json:"persistIdToken"` - PersistRefreshToken bool `schema:"persist_refresh_token_in_ldap" json:"persistRefreshToken"` + PersistIdTokenInLdap bool `schema:"persist_id_token_in_ldap" json:"persistIdTokenInLdap"` + PersistRefreshTokenInLdap bool `schema:"persist_refresh_token_in_ldap" json:"persistRefreshTokenInLdap"` AllowPostLogoutRedirectWithoutValidation bool `schema:"allow_post_logout_redirect_without_validation" json:"allowPostLogoutRedirectWithoutValidation"` InvalidateSessionCookiesAfterAuthorizationFlow bool `schema:"invalidate_session_cookies_after_authorization_flow" json:"invalidateSessionCookiesAfterAuthorizationFlow"` ReturnClientSecretOnRead bool `schema:"return_client_secret_on_read" json:"returnClientSecretOnRead"` @@ -340,6 +340,8 @@ func (c *Client) GetAppConfiguration(ctx context.Context) (*AppConfiguration, er return nil, fmt.Errorf("get request failed: %w", err) } + sortArrays(&ret.AuthorizationRequestCustomAllowedParameters) + return ret, nil } diff --git a/terraform-provider-jans/jans/app_configuration_test.go b/terraform-provider-jans/jans/app_configuration_test.go index 21b915c5223..bee553f7568 100644 --- a/terraform-provider-jans/jans/app_configuration_test.go +++ b/terraform-provider-jans/jans/app_configuration_test.go @@ -2,7 +2,10 @@ package jans import ( "context" + "fmt" + "math/rand" "testing" + "time" ) func TestAuthConfigMapping(t *testing.T) { @@ -39,7 +42,11 @@ func TestPatchAuthConfig(t *testing.T) { t.Fatal("expected 1 client in blacklist") } - cfg.ClientBlackList = []string{"*.attacker.com/*"} + rand.Seed(time.Now().UnixNano()) + + newEntry := fmt.Sprintf("*.attacker-%v.com/*", rand.Intn(100)) + + cfg.ClientBlackList = []string{newEntry} if _, err := client.UpdateAppConfiguration(ctx, cfg); err != nil { t.Fatal(err) @@ -54,7 +61,8 @@ func TestPatchAuthConfig(t *testing.T) { t.Fatal("expected 1 client in blacklist") } - if (cfg.ClientBlackList[0]) != "*.attacker.com/*" { - t.Fatal("expected *.attacker.com/* in blacklist") + if (cfg.ClientBlackList[0]) != newEntry { + t.Fatalf("expected '%s' in blacklist, got '%s'", newEntry, cfg.ClientBlackList[0]) } + } diff --git a/terraform-provider-jans/jans/client.go b/terraform-provider-jans/jans/client.go index d53c37d30c3..124bf82d7da 100644 --- a/terraform-provider-jans/jans/client.go +++ b/terraform-provider-jans/jans/client.go @@ -7,6 +7,8 @@ import ( "encoding/json" "fmt" "io" + "reflect" + "sort" "net/http" "net/url" @@ -379,3 +381,61 @@ func (c *Client) request(ctx context.Context, params requestParams) error { return nil } + +// Since some arrays in the JSON we get from the server are unsorted, +// but HCL is sorted, we sort all arrays we from the API before we +// compare them with the HCL arrays. This way we can avoid getting +// diverging plans. +func sortArrays(entity any) { + + if reflect.ValueOf(entity).Kind() != reflect.Ptr { + panic("entity is not a pointer") + } + + t := reflect.TypeOf(entity).Elem() + v := reflect.ValueOf(entity).Elem() + + if t.Kind() == reflect.Slice { + + if t.Elem().Kind() == reflect.Struct { + + // slices of structs are recursively sorted + for i := 0; i < v.Len(); i++ { + sortArrays(v.Index(i).Addr().Interface()) + } + + } + + // all slices are then sorted themselves. We use + // the string representation. More complex sorting + // can be added here if needed. + sort.Slice(v.Interface(), func(i, j int) bool { + a := fmt.Sprintf("%v", v.Index(i).Interface()) + b := fmt.Sprintf("%v", v.Index(j).Interface()) + return a < b + }) + + return + } + + if v.Kind() != reflect.Struct { + panic("entity is not a pointer to struct, nor to a slice") + } + + // iterate over all fields of the entity + for i := 0; i < v.NumField(); i++ { + + field := v.Field(i) + + // check if the field is an array + if field.Kind() == reflect.Slice { + + sort.Slice(field.Interface(), func(i, j int) bool { + a := fmt.Sprintf("%v", field.Index(i).Interface()) + b := fmt.Sprintf("%v", field.Index(j).Interface()) + return a < b + }) + } + } + +} diff --git a/terraform-provider-jans/jans/client_test.go b/terraform-provider-jans/jans/client_test.go index e816f1f6c9e..eebf664f240 100644 --- a/terraform-provider-jans/jans/client_test.go +++ b/terraform-provider-jans/jans/client_test.go @@ -8,9 +8,10 @@ import ( ) var ( - host = "" - user = "" - pass = "" + host = "" + user = "" + pass = "" + skipKnownFailures = false ) func TestMain(m *testing.M) { @@ -51,3 +52,73 @@ func TestClient(t *testing.T) { } } + +func TestSortArrays(t *testing.T) { + + cfg := AppConfiguration{ + AuthorizationEncryptionEncValuesSupported: []string{ + "A128CBC+HS256", + "A256CBC+HS512", + "A128GCM", + "A256GCM", + }, + AuthorizationRequestCustomAllowedParameters: []CustomAllowedParameter{ + { + ParamName: "customParam2", + ReturnInResponse: true, + }, + { + ParamName: "customParam1", + ReturnInResponse: false, + }, + { + ParamName: "customParam3", + ReturnInResponse: false, + }, + }, + } + + sortArrays(&cfg) + + if cfg.AuthorizationEncryptionEncValuesSupported[0] != "A128CBC+HS256" || + cfg.AuthorizationEncryptionEncValuesSupported[1] != "A128GCM" || + cfg.AuthorizationEncryptionEncValuesSupported[2] != "A256CBC+HS512" || + cfg.AuthorizationEncryptionEncValuesSupported[3] != "A256GCM" { + t.Errorf("unexpected value in AuthorizationEncryptionEncValuesSupported: %#v", cfg.AuthorizationEncryptionEncValuesSupported) + } + + if cfg.AuthorizationRequestCustomAllowedParameters[0].ParamName != "customParam1" || + cfg.AuthorizationRequestCustomAllowedParameters[1].ParamName != "customParam2" || + cfg.AuthorizationRequestCustomAllowedParameters[2].ParamName != "customParam3" { + t.Errorf("unexpected value in AuthorizationRequestCustomAllowedParameters: %#v", cfg.AuthorizationRequestCustomAllowedParameters) + } + + arr := []AdminUIRolePermissionMapping{ + { + Role: "admin", + Permissions: []string{ + "permission3", + "permission1", + "permission2", + }, + }, + { + Role: "user", + Permissions: []string{ + "permission2", + "permission3", + "permission1", + }, + }, + } + + sortArrays(&arr) + + for _, v := range arr { + if v.Permissions[0] != "permission1" || + v.Permissions[1] != "permission2" || + v.Permissions[2] != "permission3" { + t.Errorf("unexpected value in Permissions: %#v", v.Permissions) + } + } +} diff --git a/terraform-provider-jans/jans/custom_user_test.go b/terraform-provider-jans/jans/custom_user_test.go index c34a586e658..2e0a24c69d1 100644 --- a/terraform-provider-jans/jans/custom_user_test.go +++ b/terraform-provider-jans/jans/custom_user_test.go @@ -95,7 +95,8 @@ func TestCustomUsers(t *testing.T) { filter := cmp.FilterPath(func(p cmp.Path) bool { attr := p.String() return attr == "CreatedAt" || attr == "BaseDn" || attr == "Dn" || - attr == "UserPassword" || attr == "Inum" || attr == "UpdatedAt" + attr == "UserPassword" || attr == "Inum" || attr == "UpdatedAt" || + attr == "DisplayValue" }, cmp.Ignore()) if diff := cmp.Diff(&usr, createdUser, filter); diff != "" { diff --git a/terraform-provider-jans/jans/fido2_config_test.go b/terraform-provider-jans/jans/fido2_config_test.go index efa656e794e..4f318e4cfb6 100644 --- a/terraform-provider-jans/jans/fido2_config_test.go +++ b/terraform-provider-jans/jans/fido2_config_test.go @@ -9,6 +9,10 @@ import ( func TestFido2Config(t *testing.T) { + if skipKnownFailures { + t.SkipNow() + } + client, err := NewInsecureClient(host, user, pass) if err != nil { t.Fatal(err) diff --git a/terraform-provider-jans/jans/fido2_configuration_test.go b/terraform-provider-jans/jans/fido2_configuration_test.go index b7b0d30211b..a6685d199fb 100644 --- a/terraform-provider-jans/jans/fido2_configuration_test.go +++ b/terraform-provider-jans/jans/fido2_configuration_test.go @@ -7,6 +7,10 @@ import ( func TestFido2Configuration(t *testing.T) { + if skipKnownFailures { + t.SkipNow() + } + client, err := NewInsecureClient(host, user, pass) if err != nil { t.Fatal(err) diff --git a/terraform-provider-jans/jans/fido2_device_test.go b/terraform-provider-jans/jans/fido2_device_test.go index de04f806345..2c15be75d13 100644 --- a/terraform-provider-jans/jans/fido2_device_test.go +++ b/terraform-provider-jans/jans/fido2_device_test.go @@ -7,6 +7,10 @@ import ( func TestFido2Devices(t *testing.T) { + if skipKnownFailures { + t.SkipNow() + } + client, err := NewInsecureClient(host, user, pass) if err != nil { t.Fatal(err) diff --git a/terraform-provider-jans/jans/group_test.go b/terraform-provider-jans/jans/group_test.go index daa014d1cde..2534e5ac9ae 100644 --- a/terraform-provider-jans/jans/group_test.go +++ b/terraform-provider-jans/jans/group_test.go @@ -7,6 +7,10 @@ import ( func TestGroup(t *testing.T) { + if skipKnownFailures { + t.SkipNow() + } + client, err := NewInsecureClient(host, user, pass) if err != nil { t.Fatal(err) @@ -16,7 +20,7 @@ func TestGroup(t *testing.T) { _, err = client.GetGroups(ctx) if err != nil { - t.Fatal(err) + t.Error(err) } newGroup := Group{ diff --git a/terraform-provider-jans/jans/oidc_client_test.go b/terraform-provider-jans/jans/oidc_client_test.go index f3232e9d6d6..052a61ddb8c 100644 --- a/terraform-provider-jans/jans/oidc_client_test.go +++ b/terraform-provider-jans/jans/oidc_client_test.go @@ -19,6 +19,7 @@ func TestSimpleCreate(t *testing.T) { newClient := OidcClient{ RedirectUris: []string{"https://moabu-diverse-tiger.gluu.info"}, + GrantTypes: []string{}, } createdClient, err := client.CreateOidcClient(ctx, &newClient) @@ -30,7 +31,16 @@ func TestSimpleCreate(t *testing.T) { _ = client.DeleteOidcClient(ctx, createdClient.Inum) }) - if diff := cmp.Diff(&newClient, createdClient); diff != "" { + // don't compare attributes that are generated by the server + filter := cmp.FilterPath(func(p cmp.Path) bool { + attr := p.String() + return attr == "CustomAttributes" || attr == "Dn" || attr == "BaseDn" || + attr == "Inum" || attr == "ClientSecret" || attr == "ApplicationType" || + attr == "SubjectType" || attr == "TokenEndpointAuthMethod" || + attr == "Attributes" || attr == "AuthenticationMethod" + }, cmp.Ignore()) + + if diff := cmp.Diff(&newClient, createdClient, filter); diff != "" { t.Errorf("Got different configuration after create: %s", diff) } @@ -171,7 +181,12 @@ func TestOIDCClient(t *testing.T) { _ = client.DeleteOidcClient(ctx, "1201.d52300ed-8193-510e-b31d-5829f4af346e") }) - if diff := cmp.Diff(&newClient, createdClient); diff != "" { + // custom attributes are generated by the server, so we need to ignore them + filter := cmp.FilterPath(func(p cmp.Path) bool { + return p.String() == "CustomAttributes" + }, cmp.Ignore()) + + if diff := cmp.Diff(&newClient, createdClient, filter); diff != "" { t.Errorf("Got different configuration after create: %s", diff) } diff --git a/terraform-provider-jans/jans/scripts_test.go b/terraform-provider-jans/jans/scripts_test.go index d1e81c2f754..d0611c47c51 100644 --- a/terraform-provider-jans/jans/scripts_test.go +++ b/terraform-provider-jans/jans/scripts_test.go @@ -38,12 +38,12 @@ func TestScripts(t *testing.T) { Enabled: true, Modified: false, Internal: false, - LocationType: "ldap", + LocationType: "db", BaseDN: "inum=4A4E-4F3D,ou=scripts,o=jans", ModuleProperties: []SimpleCustomProperty{ { Value1: "location_type", - Value2: "ldap", + Value2: "db", }, { Value1: "location_option", @@ -62,7 +62,12 @@ func TestScripts(t *testing.T) { t.Fatal(err) } - if diff := cmp.Diff(newScript, loadScript); diff != "" { + // for new script a default source code is set, so we need to ignore it + filter := cmp.FilterPath(func(p cmp.Path) bool { + return p.String() == "Script" + }, cmp.Ignore()) + + if diff := cmp.Diff(newScript, loadScript, filter); diff != "" { t.Errorf("Got different script after mapping: %s", diff) } diff --git a/terraform-provider-jans/jans/smtp_config.go b/terraform-provider-jans/jans/smtp_config.go index b8ffaffbb3a..4177a7d2e8a 100644 --- a/terraform-provider-jans/jans/smtp_config.go +++ b/terraform-provider-jans/jans/smtp_config.go @@ -35,23 +35,6 @@ func (c *Client) GetSMTPConfiguration(ctx context.Context) (*SMTPConfiguration, return ret, nil } -// CreateSMTPConfiguration creates a new SMTP configuration. -func (c *Client) CreateSMTPConfiguration(ctx context.Context, config *SMTPConfiguration) (*SMTPConfiguration, error) { - - token, err := c.getToken(ctx, "https://jans.io/oauth/config/smtp.write") - if err != nil { - return nil, fmt.Errorf("failed to get token: %w", err) - } - - ret := &SMTPConfiguration{} - - if err := c.post(ctx, "/jans-config-api/api/v1/config/smtp", token, config, ret); err != nil { - return nil, fmt.Errorf("post request failed: %w", err) - } - - return ret, nil -} - // UpdateSMTPConfiguration updates an existing SMTP configuration. func (c *Client) UpdateSMTPConfiguration(ctx context.Context, config *SMTPConfiguration) (*SMTPConfiguration, error) { @@ -68,18 +51,3 @@ func (c *Client) UpdateSMTPConfiguration(ctx context.Context, config *SMTPConfig return ret, nil } - -// DeleteSMTPConfiguration deletes an existing SMTP configuration. -func (c *Client) DeleteSMTPConfiguration(ctx context.Context) error { - - token, err := c.getToken(ctx, "https://jans.io/oauth/config/smtp.delete") - if err != nil { - return fmt.Errorf("failed to get token: %w", err) - } - - if err := c.delete(ctx, "/jans-config-api/api/v1/config/smtp", token); err != nil { - return fmt.Errorf("delete request failed: %w", err) - } - - return nil -} diff --git a/terraform-provider-jans/jans/user_test.go b/terraform-provider-jans/jans/user_test.go index ac9bbdc2ab6..0867494400f 100644 --- a/terraform-provider-jans/jans/user_test.go +++ b/terraform-provider-jans/jans/user_test.go @@ -173,6 +173,10 @@ func TestUser(t *testing.T) { func TestGroupAssignment(t *testing.T) { + if skipKnownFailures { + t.SkipNow() + } + client, err := NewInsecureClient(host, user, pass) if err != nil { t.Fatal(err) diff --git a/terraform-provider-jans/provider/entity_mapper.go b/terraform-provider-jans/provider/entity_mapper.go index b63beebff42..eb2ef60aa9f 100644 --- a/terraform-provider-jans/provider/entity_mapper.go +++ b/terraform-provider-jans/provider/entity_mapper.go @@ -330,48 +330,9 @@ func setFieldValue(f reflect.Value, v interface{}) error { f.Set(valueToSet) case reflect.Slice: - // if the field is a slice, we need to initialize a new slice of - // the correct type and add all items to it - - elemType := vType.Elem() - - // create a new slice of the correct type - elemSlice := reflect.New(reflect.SliceOf(elemType)).Elem() - - // if the elements in the slice are structs, we need to map them back - // from the map to the struct first, before we can add them to the slice - if vType.Elem().Kind() == reflect.Struct { - - for _, elem := range v.([]interface{}) { - - m, ok := elem.(map[string]interface{}) - if !ok { - return fmt.Errorf("failed to convert value to map") - } - - // create a new struct of the correct type - newElem := reflect.New(elemType) - - getter := func(key string) (any, bool) { - val, ok := m[key] - return val, ok - } - - if err := decoder(getter, newElem.Interface()); err != nil { - return fmt.Errorf("failed to convert map to struct: %w", err) - } - - elemSlice = reflect.Append(elemSlice, reflect.Indirect(newElem)) - } - - } else { - - // otherwise we can just fill the slice, assuming it's - // a slice of primitive types - for _, elem := range v.([]interface{}) { - elemSlice = reflect.Append(elemSlice, reflect.ValueOf(elem)) - } - + elemSlice, err := convertSlice(vType, v) + if err != nil { + return fmt.Errorf("failed to convert slice: %w", err) } f.Set(elemSlice) @@ -424,3 +385,70 @@ func setFieldValue(f reflect.Value, v interface{}) error { return nil } + +// convertSlice will take the provided value and convert it to a slice of the +// correct type. If the elements in the slice are structs, they will be +// converted from a map to a struct first. If the value is nil, an empty slice +// will be returned. For slices, this will run recursively. +func convertSlice(targetType reflect.Type, value interface{}) (reflect.Value, error) { + + // if the field is a slice, we need to initialize a new slice of + // the correct type and add all items to it + + elemType := targetType.Elem() + + // create a new slice of the correct type + elemSlice := reflect.New(reflect.SliceOf(elemType)).Elem() + + // if the elements in the slice are structs, we need to map them back + // from the map to the struct first, before we can add them to the slice + if targetType.Elem().Kind() == reflect.Struct { + + for _, elem := range value.([]interface{}) { + + m, ok := elem.(map[string]interface{}) + if !ok { + return reflect.ValueOf(nil), fmt.Errorf("failed to convert value to map") + } + + // create a new struct of the correct type + newElem := reflect.New(elemType) + + getter := func(key string) (any, bool) { + val, ok := m[key] + return val, ok + } + + if err := decoder(getter, newElem.Interface()); err != nil { + return reflect.ValueOf(nil), fmt.Errorf("failed to convert map to struct: %w", err) + } + + elemSlice = reflect.Append(elemSlice, reflect.Indirect(newElem)) + } + + } else if targetType.Elem().Kind() == reflect.Slice { + + // nested slices have to be handled separately from primitive types + + subType := reflect.SliceOf(elemType.Elem()) + for _, elem := range value.([]interface{}) { + val, err := convertSlice(subType, elem) + if err != nil { + return reflect.ValueOf(nil), fmt.Errorf("failed to convert slice: %w", err) + } + + elemSlice = reflect.Append(elemSlice, val) + } + + } else { + + // otherwise we can just fill the slice, assuming it's + // a slice of primitive types + for _, elem := range value.([]interface{}) { + elemSlice = reflect.Append(elemSlice, reflect.ValueOf(elem)) + } + + } + + return elemSlice, nil +} diff --git a/terraform-provider-jans/provider/resource_app_configuration.go b/terraform-provider-jans/provider/resource_app_configuration.go index 241a82d5315..26f4a46b27c 100644 --- a/terraform-provider-jans/provider/resource_app_configuration.go +++ b/terraform-provider-jans/provider/resource_app_configuration.go @@ -346,14 +346,23 @@ func resourceAppConfiguration() *schema.Resource { }, }, "response_modes_supported": { - Type: schema.TypeList, - Optional: true, - Description: `A list of the OAuth 2.0 Response Mode values that this OP supports. One of "query¡¡", "fragment", "form_post".`, + Type: schema.TypeList, + Optional: true, + Description: `A list of the OAuth 2.0 Response Mode values that this OP supports. One of "query", + "fragment", "form_post", "query.jwt", "fragment.jwt", "form_post.jwt", "jwt".`, Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: func(v interface{}, p cty.Path) diag.Diagnostics { - enums := []string{"query¡¡", "fragment", "form_post"} + enums := []string{ + "query", + "fragment", + "form_post", + "query.jwt", + "fragment.jwt", + "form_post.jwt", + "jwt", + } return validateEnum(v, enums) }, @@ -362,15 +371,23 @@ func resourceAppConfiguration() *schema.Resource { "grant_types_supported": { Type: schema.TypeList, Optional: true, - Description: `A list of the OAuth 2.0 Grant Type values that this OP supports. One of "authorization_code", - "implicit", "password", "client_credentials", "refresh_token", "urn:ietf:params:oauth:grant-type:uma-ticket", + Description: `A list of the OAuth 2.0 Grant Type values that this OP supports. One of"none", "authorization_code", + "client_credentials", "implicit", "password", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code", + "urn:ietf:params:oauth:grant-type:token-exchange", "urn:ietf:params:oauth:grant-type:uma-ticket", "urn:openid:params:grant-type:ciba".`, Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: func(v interface{}, p cty.Path) diag.Diagnostics { - enums := []string{"authorization_code", "implicit", "password", - "client_credentials", "refresh_token", + enums := []string{ + "none", + "authorization_code", + "client_credentials", + "implicit", + "password", + "refresh_token", + "urn:ietf:params:oauth:grant-type:device_code", + "urn:ietf:params:oauth:grant-type:token-exchange", "urn:ietf:params:oauth:grant-type:uma-ticket", "urn:openid:params:grant-type:ciba", } @@ -554,15 +571,9 @@ func resourceAppConfiguration() *schema.Resource { "token_endpoint_auth_methods_supported": { Type: schema.TypeList, Optional: true, - Description: "A list of Client Authentication methods supported by this Token Endpoint. One of 'client_secret_basic', 'client_secret_post', 'client_secret_jwt', 'private_key_jwt'.", + Description: "A list of Client Authentication methods supported by this Token Endpoint.", Elem: &schema.Schema{ Type: schema.TypeString, - ValidateDiagFunc: func(v interface{}, p cty.Path) diag.Diagnostics { - - enums := []string{"client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt"} - - return validateEnum(v, enums) - }, }, }, "token_endpoint_auth_signing_alg_values_supported": { @@ -665,12 +676,12 @@ func resourceAppConfiguration() *schema.Resource { "ui_locales_supported": { Type: schema.TypeList, Optional: true, - Description: "Languages and scripts supported for the user interface. One of 'en', 'es'.", + Description: `Languages and scripts supported for the user interface. One of "en", "bg", "de", "es", "fr", "it", "ru", "tr".`, Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: func(v interface{}, p cty.Path) diag.Diagnostics { - enums := []string{"en", "es"} + enums := []string{"en", "bg", "de", "es", "fr", "it", "ru", "tr"} return validateEnum(v, enums) }, @@ -1223,7 +1234,7 @@ func resourceAppConfiguration() *schema.Resource { "authorization_request_custom_allowed_parameters": { Type: schema.TypeList, Optional: true, - Description: "Authorization Request Custom Allowed Parameters.", + Description: "Authorization Request Custom Allowed Parameters. To avoid diverging state, those should be defined in alphabetical order.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "param_name": { diff --git a/terraform-provider-jans/provider/resource_app_configuration_test.go b/terraform-provider-jans/provider/resource_app_configuration_test.go index c530b4b85be..cb574a4229b 100644 --- a/terraform-provider-jans/provider/resource_app_configuration_test.go +++ b/terraform-provider-jans/provider/resource_app_configuration_test.go @@ -46,6 +46,10 @@ func TestResourceAuthServiceConfig_Mapping(t *testing.T) { "refresh_token", "client_credentials", }, + ResponseTypesSupported: [][]string{ + {"code", "token"}, + {"code", "id_token"}, + }, } if err := toSchemaResource(data, authConfig); err != nil { diff --git a/terraform-provider-jans/terraform-registry-manifest.json b/terraform-provider-jans/terraform-registry-manifest.json new file mode 100644 index 00000000000..295001a07f7 --- /dev/null +++ b/terraform-provider-jans/terraform-registry-manifest.json @@ -0,0 +1,6 @@ +{ + "version": 1, + "metadata": { + "protocol_versions": ["6.0"] + } +}