diff --git a/ziti/cmd/edge/create.go b/ziti/cmd/edge/create.go index b40513d6a..e159d08ac 100644 --- a/ziti/cmd/edge/create.go +++ b/ziti/cmd/edge/create.go @@ -56,6 +56,7 @@ func newCreateCmd(out io.Writer, errOut io.Writer) *cobra.Command { cmd.AddCommand(newCreateTerminatorCmd(out, errOut)) cmd.AddCommand(newCreateTransitRouterCmd(out, errOut)) cmd.AddCommand(newCreateExtJwtSignerCmd(out, errOut)) + cmd.AddCommand(newCreateAuthPolicyCmd(out, errOut)) return cmd } diff --git a/ziti/cmd/edge/create_authpolicy.go b/ziti/cmd/edge/create_authpolicy.go new file mode 100644 index 000000000..d76b36193 --- /dev/null +++ b/ziti/cmd/edge/create_authpolicy.go @@ -0,0 +1,143 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package edge + +import ( + "fmt" + "github.com/openziti/edge-api/rest_management_api_client/auth_policy" + "github.com/openziti/edge-api/rest_model" + "github.com/openziti/ziti/ziti/cmd/api" + cmdhelper "github.com/openziti/ziti/ziti/cmd/helpers" + "github.com/openziti/ziti/ziti/util" + "github.com/spf13/cobra" + "io" +) + +type createAuthPolicyOptions struct { + api.EntityOptions + AuthPolicy rest_model.AuthPolicyCreate +} + +func newCreateAuthPolicyCmd(out io.Writer, errOut io.Writer) *cobra.Command { + options := &createAuthPolicyOptions{ + EntityOptions: api.NewEntityOptions(out, errOut), + AuthPolicy: rest_model.AuthPolicyCreate{ + Name: Ptr(""), + Primary: &rest_model.AuthPolicyPrimary{ + Cert: &rest_model.AuthPolicyPrimaryCert{ + AllowExpiredCerts: Ptr(false), + Allowed: Ptr(false), + }, + ExtJWT: &rest_model.AuthPolicyPrimaryExtJWT{ + Allowed: Ptr(false), + AllowedSigners: []string{}, + }, + Updb: &rest_model.AuthPolicyPrimaryUpdb{ + Allowed: Ptr(false), + LockoutDurationMinutes: Ptr(int64(0)), + MaxAttempts: Ptr(int64(0)), + MinPasswordLength: Ptr(int64(5)), + RequireMixedCase: Ptr(false), + RequireNumberChar: Ptr(false), + RequireSpecialChar: Ptr(false), + }, + }, + Secondary: &rest_model.AuthPolicySecondary{ + RequireExtJWTSigner: Ptr(""), + RequireTotp: Ptr(false), + }, + Tags: &rest_model.Tags{SubTags: map[string]interface{}{}}, + }, + } + + cmd := &cobra.Command{ + Use: "auth-policy ", + Short: "creates an authentication policy managed by the Ziti Edge Controller", + Long: "creates an authentication policy managed by the Ziti Edge Controller", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return fmt.Errorf("requires 1 arg, received %d", len(args)) + } + + options.AuthPolicy.Name = &args[0] + + return nil + + }, + Run: func(cmd *cobra.Command, args []string) { + options.Cmd = cmd + options.Args = args + err := runCreateAuthPolicy(options) + + cmdhelper.CheckErr(err) + }, + SuggestFor: []string{}, + } + + // allow interspersing positional args and flags + cmd.Flags().SetInterspersed(true) + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Cert.Allowed, "primary-cert-allowed", false, "Enable certificate authentication") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Cert.AllowExpiredCerts, "primary-cert-expired-allowed", false, "Allow expired certificates") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.ExtJWT.Allowed, "primary-ext-jwt-allowed", false, "Allow external JWT authentication") + cmd.Flags().StringArrayVar(&options.AuthPolicy.Primary.ExtJWT.AllowedSigners, "primary-ext-jwt-allowed-signers", []string{}, "Allow specific JWT signers") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Updb.Allowed, "primary-updb-allowed", false, "Allow username/password db authentication") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Updb.RequireSpecialChar, "primary-updb-req-special", false, "Require special characters in passwords") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Updb.RequireNumberChar, "primary-updb-req-numbers", false, "Require numbers in passwords") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Updb.RequireMixedCase, "primary-updb-req-mixed-case", false, "Require mixed case in passwords") + cmd.Flags().Int64Var(options.AuthPolicy.Primary.Updb.LockoutDurationMinutes, "primary-updb-lockout-min", 0, "Lockout duration minutes after max attempts, 0=forever") + cmd.Flags().Int64Var(options.AuthPolicy.Primary.Updb.MaxAttempts, "primary-updb-max-attempts", 0, "Number of invalid authentication attempts, 0=unlimited") + cmd.Flags().Int64Var(options.AuthPolicy.Primary.Updb.MinPasswordLength, "primary-updb-min-length", 5, "Minimum password length") + + cmd.Flags().StringVar(options.AuthPolicy.Secondary.RequireExtJWTSigner, "secondary-req-ext-jwt-signer", "", "JWT required on every request") + cmd.Flags().BoolVar(options.AuthPolicy.Secondary.RequireTotp, "secondary-req-totp", false, "MFA TOTP enrollment required") + options.AddCommonFlags(cmd) + + return cmd +} + +func runCreateAuthPolicy(options *createAuthPolicyOptions) (err error) { + managementClient, err := util.NewEdgeManagementClient(options) + + if err != nil { + return err + } + + for k, v := range options.GetTags() { + options.AuthPolicy.Tags.SubTags[k] = v + } + + if options.AuthPolicy.Secondary.RequireExtJWTSigner != nil && *options.AuthPolicy.Secondary.RequireExtJWTSigner == "" { + options.AuthPolicy.Secondary.RequireExtJWTSigner = nil + } + + params := auth_policy.NewCreateAuthPolicyParams() + params.AuthPolicy = &options.AuthPolicy + + resp, err := managementClient.AuthPolicy.CreateAuthPolicy(params, nil) + + if err != nil { + return util.WrapIfApiError(err) + } + + checkId := resp.GetPayload().Data.ID + + if _, err = fmt.Fprintf(options.Out, "%v\n", checkId); err != nil { + panic(err) + } + + return err +} diff --git a/ziti/cmd/edge/create_extjwtsigner.go b/ziti/cmd/edge/create_extjwtsigner.go index 75dcdd463..60840a79f 100644 --- a/ziti/cmd/edge/create_extjwtsigner.go +++ b/ziti/cmd/edge/create_extjwtsigner.go @@ -58,9 +58,10 @@ func newCreateExtJwtSignerCmd(out io.Writer, errOut io.Writer) *cobra.Command { } cmd := &cobra.Command{ - Use: "ext-jwt-signer (-u |-p |-f ) [-a -c -xe]", - Short: "creates an external JWT signer managed by the Ziti Edge Controller", - Long: "creates an external JWT signer managed by the Ziti Edge Controller", + Use: "ext-jwt-signer (-u |-p |-f ) [-a -c -xe]", + Short: "creates an external JWT signer managed by the Ziti Edge Controller", + Long: "creates an external JWT signer managed by the Ziti Edge Controller", + Aliases: []string{"external-jwt-signer"}, Args: func(cmd *cobra.Command, args []string) error { if len(args) != 2 { return fmt.Errorf("requires 2 arg, received %d", len(args)) @@ -106,17 +107,13 @@ func runCreateExtJwtSigner(options *createExtJwtSignerOptions) (err error) { } hasJwks := options.JwksEndpoint != "" - hasCertPem := options.ExtJwtSigner.CertPem != nil || *options.ExtJwtSigner.CertPem == "" + hasCertPem := options.ExtJwtSigner.CertPem != nil && *options.ExtJwtSigner.CertPem != "" hasCertPath := options.CertFilePath != "" - if (hasJwks && !hasCertPem && !hasCertPath) || (hasCertPem && !hasJwks && !hasCertPath) || (hasCertPath && !hasJwks && !hasCertPem) { + if (hasJwks && !(!hasCertPem && !hasCertPath)) || (hasCertPem && !(!hasJwks && !hasCertPath)) || (hasCertPath && !(!hasJwks && !hasCertPem)) { return errors.New("must specify only one certificate source (JWKS, cert file, or inline cert PEM") } - if (hasCertPem || hasCertPath) && (options.ExtJwtSigner.Kid == nil || *options.ExtJwtSigner.Kid == "") { - return errors.New("must specify a KID if using a certificate file or inline PEM") - } - if options.ExtJwtSigner.Audience == nil || *options.ExtJwtSigner.Audience == "" { return errors.New("audience must be specified") } diff --git a/ziti/cmd/edge/delete.go b/ziti/cmd/edge/delete.go index faa3b8022..b4fd04c0d 100644 --- a/ziti/cmd/edge/delete.go +++ b/ziti/cmd/edge/delete.go @@ -67,6 +67,8 @@ func newDeleteCmd(out io.Writer, errOut io.Writer) *cobra.Command { cmd.AddCommand(newDeleteCmdForEntityType("session", newOptions())) cmd.AddCommand(newDeleteCmdForEntityType("terminator", newOptions())) cmd.AddCommand(newDeleteCmdForEntityType("transit-router", newOptions())) + cmd.AddCommand(newDeleteCmdForEntityType("auth-policy", newOptions())) + cmd.AddCommand(newDeleteCmdForEntityType("external-jwt-signer", newOptions(), "ext-jwt-signer", "ext-jwt-signers", "external-jwt-signers")) return cmd } diff --git a/ziti/cmd/edge/list.go b/ziti/cmd/edge/list.go index e067e2608..7b2f6feca 100644 --- a/ziti/cmd/edge/list.go +++ b/ziti/cmd/edge/list.go @@ -22,6 +22,7 @@ import ( "github.com/go-openapi/runtime" "github.com/jedib0t/go-pretty/v6/table" "github.com/jedib0t/go-pretty/v6/text" + "github.com/openziti/edge-api/rest_management_api_client/auth_policy" "github.com/openziti/edge-api/rest_management_api_client/external_jwt_signer" "github.com/openziti/foundation/v2/stringz" "github.com/openziti/ziti/ziti/cmd/api" @@ -68,7 +69,7 @@ func newListCmd(out io.Writer, errOut io.Writer) *cobra.Command { cmd.AddCommand(newListEdgeRoutersCmd(newOptions())) cmd.AddCommand(newListCmdForEntityType("edge-router-policies", runListEdgeRouterPolicies, newOptions(), "erps")) cmd.AddCommand(newListCmdForEntityType("enrollments", runListEnrollments, newOptions())) - cmd.AddCommand(newListCmdForEntityType("ext-jwt-signers", runListExtJwtSigners, newOptions())) + cmd.AddCommand(newListCmdForEntityType("ext-jwt-signers", runListExtJwtSigners, newOptions(), "external-jwt-signers")) cmd.AddCommand(newListCmdForEntityType("terminators", runListTerminators, newOptions())) cmd.AddCommand(newListIdentitiesCmd(newOptions())) cmd.AddCommand(newListServicesCmd(newOptions())) @@ -480,6 +481,100 @@ func outputAuthenticators(o *api.Options, children []*gabs.Container, pagingInfo return nil } +func runListAuthPolicies(options *api.Options) error { + + client, err := util.NewEdgeManagementClient(options) + + if err != nil { + return err + } + + var filter *string = nil + + if len(options.Args) > 0 { + filter = &options.Args[0] + } + + params := auth_policy.NewListAuthPoliciesParams() + params.Filter = filter + + result, err := client.AuthPolicy.ListAuthPolicies(params, nil) + + if err != nil { + return util.WrapIfApiError(err) + } + + if options.OutputJSONResponse { + return nil + } + + payload := result.GetPayload() + + if payload == nil { + return errors.New("unexpected empty response payload") + } + + outTable := table.NewWriter() + outTable.SetStyle(table.StyleRounded) + outTable.Style().Options.SeparateRows = true + + rowConfigAutoMerge := table.RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignLeft} + + outTable.SetColumnConfigs([]table.ColumnConfig{ + {Number: 1, AutoMerge: true, Align: text.AlignLeft}, + {Number: 2, AutoMerge: true, Align: text.AlignLeft}, + {Number: 3, AutoMerge: true, Align: text.AlignLeft}, + {Number: 4, AutoMerge: true, Align: text.AlignLeft}, + {Number: 5, AutoMerge: true, Align: text.AlignLeft}, + }) + + outTable.AppendHeader(table.Row{"ID", "Name", "Section", "Type", "Config", "Config"}, rowConfigAutoMerge) + + for _, entity := range result.GetPayload().Data { + id := *entity.ID + name := *entity.Name + + maxAttempts := "0 (unlim)" + + if *entity.Primary.Updb.MaxAttempts != 0 { + maxAttempts = fmt.Sprintf("%d", *entity.Primary.Updb.MaxAttempts) + } + + lockout := "0 (forever)" + + if *entity.Primary.Updb.MaxAttempts != 0 { + lockout = fmt.Sprintf("%d", *entity.Primary.Updb.LockoutDurationMinutes) + } + + outTable.AppendRow(table.Row{id, name, "Primary", "CERT", "Allowed", *entity.Primary.Cert.Allowed}, rowConfigAutoMerge) + outTable.AppendRow(table.Row{id, name, "Primary", "CERT", "Allowed Expired", *entity.Primary.Cert.AllowExpiredCerts}, rowConfigAutoMerge) + outTable.AppendRow(table.Row{id, name, "Primary", "UPDB", "Allowed", *entity.Primary.Updb.Allowed}, rowConfigAutoMerge) + outTable.AppendRow(table.Row{id, name, "Primary", "UPDB", "Max Attempts", maxAttempts}, rowConfigAutoMerge) + outTable.AppendRow(table.Row{id, name, "Primary", "UPDB", "Lockout (M)", lockout}, rowConfigAutoMerge) + outTable.AppendRow(table.Row{id, name, "Primary", "UPDB", "Min Password Len", *entity.Primary.Updb.MinPasswordLength}, rowConfigAutoMerge) + outTable.AppendRow(table.Row{id, name, "Primary", "UPDB", "Require Mix Case", *entity.Primary.Updb.RequireMixedCase}, rowConfigAutoMerge) + outTable.AppendRow(table.Row{id, name, "Primary", "UPDB", "Require Specials", *entity.Primary.Updb.RequireSpecialChar}, rowConfigAutoMerge) + outTable.AppendRow(table.Row{id, name, "Primary", "UPDB", "Require Numbers", *entity.Primary.Updb.RequireNumberChar}, rowConfigAutoMerge) + outTable.AppendRow(table.Row{id, name, "Primary", "EXT-JWT", "Allowed", *entity.Primary.ExtJWT.Allowed}, rowConfigAutoMerge) + + if len(entity.Primary.ExtJWT.AllowedSigners) > 0 { + for _, signerId := range entity.Primary.ExtJWT.AllowedSigners { + outTable.AppendRow(table.Row{id, name, "Primary", "EXT-JWT", "Allowed Signers", signerId}, rowConfigAutoMerge) + } + } else { + outTable.AppendRow(table.Row{id, name, "Primary", "EXT-JWT", "Allowed Signers", "none"}, rowConfigAutoMerge) + } + + outTable.AppendRow(table.Row{id, name, "Secondary", "TOTP MFA", "Required", *entity.Secondary.RequireTotp}, rowConfigAutoMerge) + outTable.AppendRow(table.Row{id, name, "Secondary", "EXT-JWT", "Required Signer", stringz.OrEmpty(entity.Secondary.RequireExtJWTSigner)}, rowConfigAutoMerge) + } + + pagingInfo := newPagingInfo(payload.Meta) + api.RenderTable(options, outTable, pagingInfo) + + return nil +} + func runListExtJwtSigners(options *api.Options) error { client, err := util.NewEdgeManagementClient(options) @@ -1602,50 +1697,3 @@ func postureCheckOsToStrings(osContainers []*gabs.Container) []string { return ret } - -func runListAuthPolicies(options *api.Options) error { - children, pagingInfo, err := listEntitiesWithOptions("auth-policies", options) - if err != nil { - return err - } - return outputAuthPolicies(options, children, pagingInfo) -} - -func outputAuthPolicies(options *api.Options, children []*gabs.Container, info *api.Paging) error { - if options.OutputJSONResponse { - return nil - } - - outTable := table.NewWriter() - outTable.SetStyle(table.StyleRounded) - - rowConfigAutoMerge := table.RowConfig{AutoMerge: true} - - outTable.AppendHeader(table.Row{"", "", "Primary", "Primary", "Primary", "Primary", "Primary", "Primary", "Primary", "Secondary", "Secondary"}, rowConfigAutoMerge) - outTable.AppendHeader(table.Row{"", "", "Cert", "Cert", "ExtJwt", "ExtJwt", "UPDB", "UPDB", "UPDB", "ExtJwt", "TOTP"}, rowConfigAutoMerge) - outTable.AppendHeader(table.Row{"ID", "Name", "Allowed", "Allow Expired", "Allowed", "Signers", "Allowed", "Lockout(m)", "Max Attempts", "Signer", "Required"}) - - for i, entity := range children { - json := entity.EncodeJSON() - detail := rest_model.AuthPolicyDetail{} - err := detail.UnmarshalJSON(json) - - if err != nil { - msg := "Error unmarshalling index " + strconv.Itoa(i) + ": " + err.Error() - _, _ = options.ErrOutputWriter().Write([]byte(msg)) - } else { - secondarySigner := "" - if detail.Secondary.RequireExtJWTSigner != nil { - secondarySigner = *detail.Secondary.RequireExtJWTSigner - } - - outTable.AppendRow(table.Row{*detail.ID, *detail.Name, - *detail.Primary.Cert.Allowed, *detail.Primary.Cert.AllowExpiredCerts, - *detail.Primary.ExtJWT.Allowed, detail.Primary.ExtJWT.AllowedSigners, - *detail.Primary.Updb.Allowed, *detail.Primary.Updb.LockoutDurationMinutes, *detail.Primary.Updb.MaxAttempts, - secondarySigner, *detail.Secondary.RequireTotp}) - } - } - api.RenderTable(options, outTable, info) - return nil -} diff --git a/ziti/cmd/edge/update.go b/ziti/cmd/edge/update.go index 883ade283..9d1af6aa2 100644 --- a/ziti/cmd/edge/update.go +++ b/ziti/cmd/edge/update.go @@ -52,6 +52,7 @@ func newUpdateCmd(out io.Writer, errOut io.Writer) *cobra.Command { cmd.AddCommand(newUpdateTerminatorCmd(out, errOut)) cmd.AddCommand(newUpdatePostureCheckCmd(out, errOut)) cmd.AddCommand(newUpdateExtJwtSignerCmd(out, errOut)) + cmd.AddCommand(newUpdateAuthPolicySignerCmd(out, errOut)) return cmd } diff --git a/ziti/cmd/edge/update_authpolicy.go b/ziti/cmd/edge/update_authpolicy.go new file mode 100644 index 000000000..f621eb339 --- /dev/null +++ b/ziti/cmd/edge/update_authpolicy.go @@ -0,0 +1,234 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package edge + +import ( + "fmt" + "github.com/openziti/edge-api/rest_management_api_client/auth_policy" + "github.com/openziti/edge-api/rest_model" + "github.com/openziti/ziti/ziti/cmd/api" + "github.com/openziti/ziti/ziti/cmd/helpers" + "github.com/openziti/ziti/ziti/util" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "io" +) + +type updateAuthPolicyOptions struct { + api.EntityOptions + AuthPolicy rest_model.AuthPolicyPatch + nameOrId string + newName string +} + +func newUpdateAuthPolicySignerCmd(out io.Writer, errOut io.Writer) *cobra.Command { + options := updateAuthPolicyOptions{ + EntityOptions: api.NewEntityOptions(out, errOut), + AuthPolicy: rest_model.AuthPolicyPatch{ + Name: Ptr(""), + Primary: &rest_model.AuthPolicyPrimaryPatch{ + Cert: &rest_model.AuthPolicyPrimaryCertPatch{ + AllowExpiredCerts: Ptr(false), + Allowed: Ptr(false), + }, + ExtJWT: &rest_model.AuthPolicyPrimaryExtJWTPatch{ + Allowed: Ptr(false), + AllowedSigners: []string{}, + }, + Updb: &rest_model.AuthPolicyPrimaryUpdbPatch{ + Allowed: Ptr(false), + LockoutDurationMinutes: Ptr(int64(0)), + MaxAttempts: Ptr(int64(0)), + MinPasswordLength: Ptr(int64(5)), + RequireMixedCase: Ptr(false), + RequireNumberChar: Ptr(false), + RequireSpecialChar: Ptr(false), + }, + }, + Secondary: &rest_model.AuthPolicySecondaryPatch{ + RequireExtJWTSigner: Ptr(""), + RequireTotp: Ptr(false), + }, + Tags: &rest_model.Tags{SubTags: map[string]interface{}{}}, + }, + nameOrId: "", + newName: "", + } + + cmd := &cobra.Command{ + Use: "auth-policy ", + Short: "updates an authentication policy managed by the Ziti Edge Controller", + Long: "updates an authentication policy managed by the Ziti Edge Controller", + Args: func(cmd *cobra.Command, args []string) error { + switch { + case len(args) == 0: + return fmt.Errorf("id or name is required") + case len(args) > 1: + return fmt.Errorf("too many positional arguments") + } + + options.nameOrId = args[0] + + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + options.Cmd = cmd + options.Args = args + err := runUpdateAuthPolicySigner(options) + + helpers.CheckErr(err) + }, + SuggestFor: []string{}, + } + + cmd.Flags().SetInterspersed(true) + cmd.Flags().StringVarP(&options.newName, "name", "n", "", "A new name for the entity") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Cert.Allowed, "primary-cert-allowed", false, "Enable certificate authentication") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Cert.AllowExpiredCerts, "primary-cert-expired-allowed", false, "Allow expired certificates") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.ExtJWT.Allowed, "primary-ext-jwt-allowed", false, "Allow external JWT authentication") + cmd.Flags().StringArrayVar(&options.AuthPolicy.Primary.ExtJWT.AllowedSigners, "primary-ext-jwt-allowed-signers", []string{}, "Allow specific JWT signers") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Updb.Allowed, "primary-updb-allowed", false, "Allow username/password db authentication") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Updb.RequireSpecialChar, "primary-updb-req-special", false, "Require special characters in passwords") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Updb.RequireNumberChar, "primary-updb-req-numbers", false, "Require numbers in passwords") + cmd.Flags().BoolVar(options.AuthPolicy.Primary.Updb.RequireMixedCase, "primary-updb-req-mixed-case", false, "Require mixed case in passwords") + cmd.Flags().Int64Var(options.AuthPolicy.Primary.Updb.LockoutDurationMinutes, "primary-updb-lockout-min", 0, "Lockout duration minutes after max attempts, 0=forever") + cmd.Flags().Int64Var(options.AuthPolicy.Primary.Updb.MaxAttempts, "primary-updb-max-attempts", 0, "Number of invalid authentication attempts, 0=unlimited") + cmd.Flags().Int64Var(options.AuthPolicy.Primary.Updb.MinPasswordLength, "primary-updb-min-length", 5, "Minimum password length") + + cmd.Flags().StringVar(options.AuthPolicy.Secondary.RequireExtJWTSigner, "secondary-req-ext-jwt-signer", "", "JWT required on every request") + cmd.Flags().BoolVar(options.AuthPolicy.Secondary.RequireTotp, "secondary-req-totp", false, "MFA TOTP enrollment required") + options.AddCommonFlags(cmd) + + return cmd +} + +func runUpdateAuthPolicySigner(options updateAuthPolicyOptions) error { + id, err := mapNameToID("auth-policies", options.nameOrId, options.Options) + + if err != nil { + return err + } + + client, err := util.NewEdgeManagementClient(&options) + + if err != nil { + return err + } + + changed := false + + if options.Cmd.Flag("name").Changed { + options.AuthPolicy.Name = &options.newName + changed = true + } else { + options.AuthPolicy.Name = nil + } + + if options.Cmd.Flag("primary-cert-allowed").Changed { + changed = true + } else { + options.AuthPolicy.Primary.Cert.Allowed = nil + } + + if options.Cmd.Flag("primary-cert-expired-allowed").Changed { + changed = true + } else { + options.AuthPolicy.Primary.Cert.AllowExpiredCerts = nil + } + + if options.Cmd.Flag("primary-ext-jwt-allowed").Changed { + changed = true + } else { + options.AuthPolicy.Primary.ExtJWT.Allowed = nil + } + + if options.Cmd.Flag("primary-ext-jwt-allowed-signers").Changed { + changed = true + } else { + options.AuthPolicy.Primary.ExtJWT.AllowedSigners = nil + } + + if options.Cmd.Flag("primary-updb-allowed").Changed { + changed = true + } else { + options.AuthPolicy.Primary.Updb.Allowed = nil + } + + if options.Cmd.Flag("primary-updb-req-special").Changed { + changed = true + } else { + options.AuthPolicy.Primary.Updb.RequireSpecialChar = nil + } + + if options.Cmd.Flag("primary-updb-req-numbers").Changed { + changed = true + } else { + options.AuthPolicy.Primary.Updb.RequireNumberChar = nil + } + + if options.Cmd.Flag("primary-updb-req-mixed-case").Changed { + changed = true + } else { + options.AuthPolicy.Primary.Updb.RequireMixedCase = nil + } + + if options.Cmd.Flag("primary-updb-lockout-min").Changed { + changed = true + } else { + options.AuthPolicy.Primary.Updb.LockoutDurationMinutes = nil + } + + if options.Cmd.Flag("primary-updb-max-attempts").Changed { + changed = true + } else { + options.AuthPolicy.Primary.Updb.MaxAttempts = nil + } + + if options.Cmd.Flag("primary-updb-min-length").Changed { + changed = true + } else { + options.AuthPolicy.Primary.Updb.MinPasswordLength = nil + } + + if options.Cmd.Flag("secondary-req-ext-jwt-signer").Changed { + changed = true + } else { + options.AuthPolicy.Secondary.RequireExtJWTSigner = nil + } + + if options.Cmd.Flag("secondary-req-totp").Changed { + changed = true + } else { + options.AuthPolicy.Secondary.RequireTotp = nil + } + + if !changed { + return errors.New("no values changed") + } + + params := auth_policy.NewPatchAuthPolicyParams() + params.AuthPolicy = &options.AuthPolicy + params.ID = id + + _, err = client.AuthPolicy.PatchAuthPolicy(params, nil) + + if err != nil { + return util.WrapIfApiError(err) + } + + return nil +}