diff --git a/govc/USAGE.md b/govc/USAGE.md index 440650e11..5b46afe32 100644 --- a/govc/USAGE.md +++ b/govc/USAGE.md @@ -283,6 +283,8 @@ but appear via `govc $cmd -h`: - [sso.group.rm](#ssogrouprm) - [sso.group.update](#ssogroupupdate) - [sso.idp.ls](#ssoidpls) + - [sso.lpp.info](#ssolppinfo) + - [sso.lpp.update](#ssolppupdate) - [sso.service.ls](#ssoservicels) - [sso.user.create](#ssousercreate) - [sso.user.id](#ssouserid) @@ -4783,6 +4785,44 @@ Examples: Options: ``` +## sso.lpp.info + +``` +Usage: govc sso.lpp.info [OPTIONS] + +Get SSO local password policy. + +Examples: + govc sso.lpp.info + govc sso.lpp.info -json + +Options: +``` + +## sso.lpp.update + +``` +Usage: govc sso.lpp.update [OPTIONS] NAME + +Update SSO local password policy. + +Examples: + govc sso.lpp.update -PasswordLifetimeDays 0 + +Options: + -Description= Description + -MaxIdenticalAdjacentCharacters=0 Maximum identical adjacent characters + -MaxLength=0 Maximum length + -MinAlphabeticCount= Minimum alphabetic count + -MinLength= Minimim length + -MinLowercaseCount= Minimum lowercase count + -MinNumericCount= Minimum numeric count + -MinSpecialCharCount= Minimum special characters count + -MinUppercaseCount= Minimum uppercase count + -PasswordLifetimeDays= Password lifetime days + -ProhibitedPreviousPasswordsCount=0 Prohibited previous passwords count +``` + ## sso.service.ls ``` diff --git a/govc/main.go b/govc/main.go index 6f7385642..b0a48618e 100644 --- a/govc/main.go +++ b/govc/main.go @@ -85,6 +85,7 @@ import ( _ "github.com/vmware/govmomi/govc/session" _ "github.com/vmware/govmomi/govc/sso/group" _ "github.com/vmware/govmomi/govc/sso/idp" + _ "github.com/vmware/govmomi/govc/sso/lpp" _ "github.com/vmware/govmomi/govc/sso/service" _ "github.com/vmware/govmomi/govc/sso/user" _ "github.com/vmware/govmomi/govc/storage/policy" diff --git a/govc/sso/lpp/info.go b/govc/sso/lpp/info.go new file mode 100644 index 000000000..1257d0826 --- /dev/null +++ b/govc/sso/lpp/info.go @@ -0,0 +1,111 @@ +/* +Copyright (c) 2022-2022 VMware, Inc. All Rights Reserved. + +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 + + http://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 lpp + +import ( + "context" + "flag" + "fmt" + "io" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/govc/sso" + "github.com/vmware/govmomi/ssoadmin" + "github.com/vmware/govmomi/ssoadmin/types" +) + +type info struct { + *flags.ClientFlag + *flags.OutputFlag +} + +func init() { + cli.Register("sso.lpp.info", &info{}) +} + +func (cmd *info) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) + + cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx) + cmd.OutputFlag.Register(ctx, f) +} + +func (cmd *info) Description() string { + return `Get SSO local password policy. + +Examples: + govc sso.lpp.info + govc sso.lpp.info -json` +} + +func (cmd *info) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return cmd.OutputFlag.Process(ctx) +} + +type lppInfo struct { + LocalPasswordPolicy *types.AdminPasswordPolicy +} + +func (r *lppInfo) Write(w io.Writer) error { + fmt.Fprintf( + w, + "Description: %s\n"+ + "MinLength: %d\n"+ + "MaxLength: %d\n"+ + "MinAlphabeticCount: %d\n"+ + "MinUppercaseCount: %d\n"+ + "MinLowercaseCount: %d\n"+ + "MinNumericCount: %d\n"+ + "MinSpecialCharCount: %d\n"+ + "MaxIdenticalAdjacentCharacters: %d\n"+ + "ProhibitedPreviousPasswordsCount: %d\n"+ + "PasswordLifetimeDays: %d\n", + r.LocalPasswordPolicy.Description, + r.LocalPasswordPolicy.PasswordFormat.LengthRestriction.MinLength, + r.LocalPasswordPolicy.PasswordFormat.LengthRestriction.MaxLength, + r.LocalPasswordPolicy.PasswordFormat.AlphabeticRestriction.MinAlphabeticCount, + r.LocalPasswordPolicy.PasswordFormat.AlphabeticRestriction.MinUppercaseCount, + r.LocalPasswordPolicy.PasswordFormat.AlphabeticRestriction.MinLowercaseCount, + r.LocalPasswordPolicy.PasswordFormat.MinNumericCount, + r.LocalPasswordPolicy.PasswordFormat.MinSpecialCharCount, + r.LocalPasswordPolicy.PasswordFormat.MaxIdenticalAdjacentCharacters, + r.LocalPasswordPolicy.ProhibitedPreviousPasswordsCount, + r.LocalPasswordPolicy.PasswordLifetimeDays, + ) + return nil +} + +func (r *lppInfo) Dump() interface{} { + return r.LocalPasswordPolicy +} + +func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error { + return sso.WithClient(ctx, cmd.ClientFlag, func(c *ssoadmin.Client) error { + var err error + var pol lppInfo + pol.LocalPasswordPolicy, err = c.GetLocalPasswordPolicy(ctx) + if err != nil { + return err + } + return cmd.WriteResult(&pol) + }) +} diff --git a/govc/sso/lpp/update.go b/govc/sso/lpp/update.go new file mode 100644 index 000000000..fbc5680ff --- /dev/null +++ b/govc/sso/lpp/update.go @@ -0,0 +1,120 @@ +/* +Copyright (c) 2022-2022 VMware, Inc. All Rights Reserved. + +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 + + http://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 lpp + +import ( + "context" + "flag" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/govc/sso" + "github.com/vmware/govmomi/ssoadmin" + "github.com/vmware/govmomi/ssoadmin/types" +) + +type policyDetails struct { + *flags.ClientFlag + + pol types.AdminPasswordPolicy + MinLength *int32 + MinAlphabeticCount *int32 + MinUppercaseCount *int32 + MinLowercaseCount *int32 + MinNumericCount *int32 + MinSpecialCharCount *int32 + PasswordLifetimeDays *int32 +} + +func (cmd *policyDetails) Usage() string { + return "NAME" +} + +func (cmd *policyDetails) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) + + f.StringVar(&cmd.pol.Description, "Description", "", "Description") + f.Var(flags.NewOptionalInt32(&cmd.MinLength), "MinLength", "Minimim length") + f.Var(flags.NewInt32(&cmd.pol.PasswordFormat.LengthRestriction.MaxLength), "MaxLength", "Maximum length") + f.Var(flags.NewOptionalInt32(&cmd.MinAlphabeticCount), "MinAlphabeticCount", "Minimum alphabetic count") + f.Var(flags.NewOptionalInt32(&cmd.MinUppercaseCount), "MinUppercaseCount", "Minimum uppercase count") + f.Var(flags.NewOptionalInt32(&cmd.MinLowercaseCount), "MinLowercaseCount", "Minimum lowercase count") + f.Var(flags.NewOptionalInt32(&cmd.MinNumericCount), "MinNumericCount", "Minimum numeric count") + f.Var(flags.NewOptionalInt32(&cmd.MinSpecialCharCount), "MinSpecialCharCount", "Minimum special characters count") + f.Var(flags.NewInt32(&cmd.pol.PasswordFormat.MaxIdenticalAdjacentCharacters), "MaxIdenticalAdjacentCharacters", "Maximum identical adjacent characters") + f.Var(flags.NewInt32(&cmd.pol.ProhibitedPreviousPasswordsCount), "ProhibitedPreviousPasswordsCount", "Prohibited previous passwords count") + f.Var(flags.NewOptionalInt32(&cmd.PasswordLifetimeDays), "PasswordLifetimeDays", "Password lifetime days") +} + +type update struct { + policyDetails +} + +func init() { + cli.Register("sso.lpp.update", &update{}) +} + +func (cmd *update) Description() string { + return `Update SSO local password policy. + +Examples: + govc sso.lpp.update -PasswordLifetimeDays 0` +} + +func smerge(src *string, current string) { + if *src == "" { + *src = current + } +} + +func imerge(src *int32, current int32) { + if *src == 0 { + *src = current + } +} + +func oimerge(src *int32, flag *int32, current int32) { + if flag == nil { + *src = current + } else { + *src = *flag + } +} + +func (cmd *update) Run(ctx context.Context, f *flag.FlagSet) error { + return sso.WithClient(ctx, cmd.ClientFlag, func(c *ssoadmin.Client) error { + current, err := c.GetLocalPasswordPolicy(ctx) + if err != nil { + return err + } + + smerge(&cmd.pol.Description, current.Description) + oimerge(&cmd.pol.PasswordFormat.LengthRestriction.MinLength, cmd.MinLength, current.PasswordFormat.LengthRestriction.MinLength) + imerge(&cmd.pol.PasswordFormat.LengthRestriction.MaxLength, current.PasswordFormat.LengthRestriction.MaxLength) + oimerge(&cmd.pol.PasswordFormat.AlphabeticRestriction.MinAlphabeticCount, cmd.MinAlphabeticCount, current.PasswordFormat.AlphabeticRestriction.MinAlphabeticCount) + oimerge(&cmd.pol.PasswordFormat.AlphabeticRestriction.MinUppercaseCount, cmd.MinUppercaseCount, current.PasswordFormat.AlphabeticRestriction.MinUppercaseCount) + oimerge(&cmd.pol.PasswordFormat.AlphabeticRestriction.MinLowercaseCount, cmd.MinLowercaseCount, current.PasswordFormat.AlphabeticRestriction.MinLowercaseCount) + oimerge(&cmd.pol.PasswordFormat.MinNumericCount, cmd.MinNumericCount, current.PasswordFormat.MinNumericCount) + oimerge(&cmd.pol.PasswordFormat.MinSpecialCharCount, cmd.MinSpecialCharCount, current.PasswordFormat.MinSpecialCharCount) + imerge(&cmd.pol.PasswordFormat.MaxIdenticalAdjacentCharacters, current.PasswordFormat.MaxIdenticalAdjacentCharacters) + imerge(&cmd.pol.ProhibitedPreviousPasswordsCount, current.ProhibitedPreviousPasswordsCount) + oimerge(&cmd.pol.PasswordLifetimeDays, cmd.PasswordLifetimeDays, current.PasswordLifetimeDays) + + return c.UpdateLocalPasswordPolicy(ctx, cmd.pol) + }) +} diff --git a/ssoadmin/client.go b/ssoadmin/client.go index 9049eae4e..6dc626f67 100644 --- a/ssoadmin/client.go +++ b/ssoadmin/client.go @@ -146,6 +146,16 @@ func (c *Client) CreateSolutionUser(ctx context.Context, name string, details ty return err } +func (c *Client) UpdateLocalPasswordPolicy(ctx context.Context, policy types.AdminPasswordPolicy) error { + req := types.UpdateLocalPasswordPolicy{ + This: c.ServiceContent.PasswordPolicyService, + Policy: policy, + } + + _, err := methods.UpdateLocalPasswordPolicy(ctx, c, &req) + return err +} + func (c *Client) UpdateSolutionUser(ctx context.Context, name string, details types.AdminSolutionDetails) error { req := types.UpdateLocalSolutionUserDetails{ This: c.ServiceContent.PrincipalManagementService, @@ -411,6 +421,19 @@ func (c *Client) FindParentGroups(ctx context.Context, id types.PrincipalId, gro return nil, nil } +func (c *Client) GetLocalPasswordPolicy(ctx context.Context) (*types.AdminPasswordPolicy, error) { + req := types.GetLocalPasswordPolicy{ + This: c.ServiceContent.PasswordPolicyService, + } + + res, err := methods.GetLocalPasswordPolicy(ctx, c, &req) + if err != nil { + return nil, err + } + + return &res.Returnval, nil +} + func (c *Client) Login(ctx context.Context) error { req := types.Login{ This: c.ServiceContent.SessionManager,